XDU2019寒假集训:week3 DP进阶
A:BZOJ 1264:[AHOI2006]
题意:给两个序列s1,s2,由N种元素构成,满足每个序列里各元素出现5次,求最长公共子序列
思路:每个串中的元素都会重复五次,记录a串中的位置,读b串,因为已经知道两个相等,然后比较a串前面最大的加1和这个位置的值,用树状数组存一下区间最大值,树状数组+最长公共子序列变体
[code]#include<cstdio> #define MAXN 100005 int n; int f[MAXN],pos[MAXN/5][6]; int BIT[MAXN]; int max(int x,int y) { return x>y?x:y; } void init() { scanf("%d",&n); n*=5; for(int i=1;i<=n;i++) { int x; scanf("%d",&x); pos[x][++pos[x][0]]=i; } return ; } int lowbit(int x) { return x&(-x); } int query(int x) { int res=0; for(int i=x;i;i-=lowbit(i)) res=max(res,BIT[i]); return res; } int updata(int x,int val) { for(int i=x;i<=n;i+=lowbit(i)) BIT[i]=max(BIT[i],val); return 0; } int solve() { int ans=0; for(int i=1;i<=n;i++) { int x; scanf("%d",&x); for(int j=5;j>=1;j--) { int p=pos[x][j]; f[p]B:HDU 6537=max(f[p],query(p-1)+1);//因为是和前面的最大数进行 //比较,然后此位因为相等而+1 //所以此处p需要-1, updata(p,f[p]); //之前写的时候,居然把x和p写混了 //还是需要注意的 ans=max(ans,f[p]); } } return ans; } int main() { init(); printf("%d",solve()); return 0; }
题意:给一个由整数组成的序列,可以对区间[l,r]进行反转(invert),利用反转令区间的最长不降序子序列长度增大,求最后的序列长度和反转的区间
思路:第一个想的是先找升序列,再找降序列,然后两边加一下,结果发现10^5
C:zoj3537
题意:给出一些点表示多边形蛋糕的定点的位置(逆时针顺序),切蛋糕时每次只能在顶点和顶点间切,每一次切蛋糕都有相应的代价( |xi + xj| * |yi + yj| % p.),给出代价的公式,问把蛋糕切成多个三角形的最小代价是多少。顶点数<=300(最优三角剖分)
思路:判断凸包+区间dp
[code]#include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #define ll long long #define INF 2000000007 #define MAXN 305 using namespace std; struct NOTE { int x; int y; }; NOTE poi[MAXN],sta[MAXN]; int ans[MAXN][MAXN]; int cost[MAXN][MAXN]; int n,top; bool cmp(struct NOTE a,struct NOTE b) { double anga=atan2((a.y-poi[1].y),(a.x-poi[1].x)); double angb=atan2((b.y-poi[1].y),(b.x-poi[1].x)); if(anga!=angb) return anga<angb; return a.x<b.x; } ll det(NOTE a,NOTE b,NOTE c) { return 1LL*(b.x-a.x)*(c.y-a.y)-1LL*(c.x-a.x)*(b.y-a.y); } double dis(NOTE a,NOTE b) { return sqrt(1LL*(a.x-b.x)*(a.x-b.x)+1LL*(a.y-b.y)*(a.y-b.y)); } void Graham() { struct NOTE tmp; int pos=1; tmp.x=INF,tmp.y=INF; for(int i=1;i<=n;i++) if(poi[i].y<tmp.y||(poi[i].y==tmp.y&&poi[i].x<tmp.x)) { tmp=poi[i];pos=i; } swap(poi[pos],poi[1]); sort(&poi[2],&poi[1+n],cmp); sta[++top]=poi[1]; sta[++top]=poi[2]; // printf("top==%d\n",top); for(int i=3;i<=n;) { if(top>=0&&det(sta[top],sta[top-1],poi[i])>=0) top--; else sta[++top]=poi[i++]; } } int Geta(int i,int j) { if(ans[i][j]!=-1) return ans[i][j]; if(j-i<=2) return 0; ans[i][j]=INF; for(int k=i+1;k<j;k++) { ans[i][j]=min(ans[i][j],Geta(i,k)+Geta(k,j)+cost[i][k]+cost[k][j]); } return ans[i][j]; } int main() { int p; while(~scanf("%d%d",&n,&p)) { for(int i=1;i<=n;i++) scanf("%d%d",&poi[i].x,&poi[i].y); top=0; Graham(); if(top<n) { printf("I can't cut.\n"); continue; } memset(ans,-1,sizeof(ans)); memset(cost,0,sizeof(cost)); for(int i=1;i<=n;i++) for(int j=i+2;j<=n;j++) cost[i][j]=cost[j][i]=(abs(poi[i].x+poi[j].x)*abs(poi[i].y+poi[j].y))%p; printf("%d\n",Geta(1,n)); } return 0; }
M:POJ2823
题意:n个数,长度区间为k,求所有长度为k的区间内的最大值和最小值,
思路:经典的单调队列优化dp(没看见转移方程有点懵。)
所得:单调队列求前k大(小)效果拔群
[code]#include<cstdio> #define MAXN 1000005 int n,k; int num[MAXN]; int minn[MAXN*5],maxn[MAXN*5]; int min_q[MAXN],max_q[MAXN]; int pre_high,pre_low,tail_high,tail_low; void init() { pre_high=pre_low=tail_high=tail_low=0; } void solve(int i) { int x=num[i]; while(tail_high>pre_high&&num[max_q[tail_high-1]]<x&&tail_high-1>=0) { tail_high--; } max_q[tail_high++]=i; while(tail_low>pre_low&&num[min_q[tail_low-1]]>x&&tail_low-1>=0) { tail_low--; } min_q[tail_low++]=i; if(i>=k) { if(min_q[pre_low]<i+1-k&&pre_low<=tail_low) pre_low++; minn[i+1-k]=num[min_q[pre_low]]; if(max_q[pre_high]<i+1-k&&pre_high<=tail_high) pre_high++; maxn[i+1-k]=num[max_q[pre_high]]; } } void print(int *a) { for(int i=1;i<=n-k+1;i++) { printf("%d%c",a[i],i==n-k+1?'\n':' '); } } int main() { scanf("%d%d",&n,&k); init(); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); solve(i); } print(minn); print(maxn); }
其他方法:在网上看优先队列,线段树也都可以做,有时间补
T:
题意:给定区间n~m,求其间数字不包含4和62的数字数量
思路:觉得暴力打表好写就写了居然过了。查了一下发现正解是数位dp(讲课的ppt里有),之后再学下数位dp补一下
学完之后U应该也能过了
[code]//打表代码 #include<cstdio> #define MAXN 1000005 int ans[MAXN]; void init() { for(int i=1;i<=1000000;i++) { int tmp=i; while(tmp) { if(tmp%10==4 || tmp% 100==62) { ans[i]=1; break; } tmp/=10; } ans[i]+=ans[i-1]; } } int main() { int l,r; init(); while(scanf("%d%d",&l,&r)) { if(!(l&&r)) break; printf("%d\n",r+1-l-(ans[r]-ans[l-1])); } }
U:
题意:求1~n之间有多少个数又含有13又能被13整除,(1<=n<=1e9)
思路:数位dp,具体程序注释
[code]#include<cstdio> #include<cstring> #define MAXN 20 int dp[MAXN][MAXN][10]; int maxx[MAXN]; int Geta(int len,int mod,int stu,bool lim) { if(len==0)//当所有位置上的数字列举完成之后 return mod==0&&stu==2; if(!lim && dp[len][mod][stu]) return dp[len][mod][stu]; int cnt=0,max=lim?maxx[len]:9; for(int i=0;i<=max;i++) { int ns=stu; //状态的转换 if(stu!=2&&i==1)//如果数字没有13,并且此位为1,那么标记为状态1 ns=1; if(stu!=2&&i!=1)//如果此位不为1,那么就什么都没有,即为0 ns=0; if(stu==1&&i==3)//如果前一位是1,并且这一位是3的话,这个数字就拥有了13 ns=2; //注意顺序 cnt+=Geta(len-1,(mod*10+i)%13,ns,lim&&i==max); } if(!lim)//没有超过的话,就将计数返还 dp[len][mod][stu]=cnt; return cnt; } int main() { int n; while(~scanf("%d",&n)) { memset(dp,0,sizeof(dp)); int len=0; while(n) { maxx[++len]=n%10; n/=10; } printf("%d\n",Geta(len,0,0,1)); } }
- CSU-ACM2019寒假集训 DP初步-A - 数字三角
- CSU-ACM2019寒假集训 DP初步-B - Common Subsequence
- CSU-ACM2019寒假集训 DP初步-C - Longest Ordered Subsequence
- XDU寒假集训:week2-简单搜索
- CSU-ACM2019寒假集训 DP初步-I - 石子归并
- CSU-ACM2019寒假集训 DP初步-E - Bone Collector
- 2015寒假集训--dp--数字三角形问题
- 2019牛客寒假算法基础集训5
- 2019牛客寒假算法基础集训3
- 2019牛客寒假算法基础集训2
- 2019牛客寒假算法基础集训4
- 2019牛客寒假算法基础集训1
- 编辑距离-dp进阶
- 最长公共子序列-dp进阶
- [2018雅礼集训1-23]盛大的庆典 状压DP
- SDUT_2015寒假集训_结构体练习_H-顺序建立链表
- 2018年全国多校算法寒假训练营练习比赛(第二场) E 小G有一个大树(树状DP 未解决)
- 【2018寒假集训Day 1】【位运算】生成字符串
- 2018寒假DH省选集训模拟题1
- 寒假集训.Discrete-Function