[kuangbin] 基础dp 2017-8-14
G - -免费馅饼 hdu1176
定义dp[t][x] 在t时刻位于坐标x,那么 dp[t][x]=max(dp[t-1][x-1],dp[t+1][x+1])+a[t][x];
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n; 4 int dp[100100][12]; 5 int a[100100][12]; 6 int main() 7 { 8 while(~scanf("%d",&n)&&n) 9 { 10 memset(a,0,sizeof(a)); 11 memset(dp,0,sizeof(dp)); 12 int x,t; 13 for(int i=0;i<n;i++) 14 { 15 scanf("%d%d",&x,&t); 16 a[t][x]++; 17 } 18 for(int i=1;i<100000;i++) 19 { 20 for(int j=0;j<=10;j++) 21 { 22 if(i<5&&abs(j-5)>i) continue; 23 if(j==0) dp[i][j]=max(dp[i-1][j+1],dp[i-1][j])+a[i][j]; 24 else if(j==10) dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+a[i][j]; 25 else dp[i][j]=max(dp[i-1][j],max(dp[i-1][j+1],dp[i-1][j-1]))+a[i][j]; 26 } 27 } 28 int ans=0; 29 for(int i=0;i<=10;i++) 30 ans=max(ans,dp[99999][i]); 31 printf("%d\n",ans); 32 } 33 }
I -- 最少拦截系统 hdu1257
求一个 递减序列的最少划分数目,偏序集定理 链的最小划分数等于最长反链的长度,因此求一个LIS即可
1 //求一个LIS 2 #include<bits/stdc++.h> 3 using namespace std; 4 int a[30010]; 5 int dp[30010]; 6 const int inf=40000; 7 int n; 8 int main() 9 { 10 while(~scanf("%d",&n)) 11 { 12 fill(dp,dp+n,inf); 13 for(int i=0;i<n;i++){ 14 scanf("%d",&a[i]); 15 int pos=lower_bound(dp,dp+n,a[i])-dp; 16 dp[pos]=a[i]; 17 } 18 int ans=lower_bound(dp,dp+n,inf)-dp; 19 printf("%d\n",ans); 20 } 21 }
A --
Max Sum Plus Plus
hdu1024 -- 将序列分成m段 求m段和的最大值
这里用tmp得到前i段最大值 复杂度m*n 题目m范围很小
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 typedef long long LL; 5 int m,n; 6 const int maxn=1000010; 7 LL dp[maxn]; 8 const LL inf=1e15; 9 int a[maxn]; 10 LL maxx[maxn]; 11 int main() 12 { 13 while(~scanf("%d%d",&m,&n)) 14 { 15 for(int i=1;i<=n;i++) 16 scanf("%d",&a[i]); 17 for(int i=0;i<=n;i++) maxx[i]=dp[i]=0; 18 for(int i=1;i<=m;i++) 19 { 20 LL tmp=-inf; 21 for(int j=i;j<=n;j++) 22 { 23 dp[j]=max(dp[j-1],maxx[j-1])+a[j]; 24 maxx[j-1]=tmp; 25 if(dp[j]>tmp) tmp=dp[j]; 26 } 27 } 28 LL ans=-inf; 29 for(int i=m;i<=n;i++) ans=max(ans,dp[i]); 30 printf("%lld\n",ans); 31 } 32 }
B - Ignatius and the Princess IV
hdu1029-- 有n块长方体 分别给出长宽高,每块长方体可以取无限次,现在要求取若干长方体摞起来 能产生的最大高度是多少,能将两块长方体摞起来的要求是下面的长宽要严格大于上面的长和宽,同时一个长方体可以进行各种旋转。
思路是:一个长方体经过旋转后 最多有6种形态,又因为 摞起来的要求是严格递减,所以每种形态的长方体只会使用一次,那么这个问题就转化成了 给你6*n个长方体 不可以旋转 直接摞起来 只能用一次 问可以产生的最大高度是多少 很明显这就变成了一个LIS问题
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn=50; 5 6 struct node 7 { 8 int x,y,z; 9 bool operator < (node u)const 10 { 11 return x==u.x?y>u.y:x>u.x; 12 } 13 }a[maxn*6]; 14 15 int dp[maxn*6]; 16 int n; 17 int main() 18 { 19 int res=1; 20 while(~scanf("%d",&n)&&n) 21 { 22 int x,y,z; 23 int to=1; 24 for(int i=0;i<n;i++) 25 { 26 scanf("%d%d%d",&x,&y,&z); 27 a[to].x=x,a[to].y=y,a[to].z=z,to++; 28 a[to].x=x,a[to].y=z,a[to].z=y,to++; 29 a[to].x=y,a[to].y=x,a[to].z=z,to++; 30 a[to].x=y,a[to].y=z,a[to].z=x,to++; 31 a[to].x=z,a[to].y=x,a[to].z=y,to++; 32 a[to].x=z,a[to].y=y,a[to].z=x,to++; 33 } 34 sort(a+1,a+1+to); 35 dp[0]=0; 36 int ans=0; 37 for(int i=1;i<to;i++) 38 { 39 int tmp=0; 40 for(int j=0;j<i;j++) 41 { 42 if(a[j].x>a[i].x&&a[j].y>a[i].y) 43 tmp=max(tmp,dp[j]); 44 } 45 dp[i]=tmp+a[i].z; 46 ans=max(dp[i],ans); 47 } 48 printf("Case %d: maximum height = %d\n",res++,ans); 49 } 50 51 }
E - Super Jumping! Jumping! Jumping!
题意是 给你一个不超过1000个的序列 每个位置有一个值,从最左端 在这个序列上走 要求下一个位置必须比当前位置的
值要大,定义获得的价值为经过的地方的值的加和,求获得的最大值为多少
LIS
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n; 4 const int maxn=1010; 5 int dp[maxn]; 6 int a[maxn]; 7 8 int main() 9 { 10 while(~scanf("%d",&n)&&n) 11 { 12 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 13 memset(dp,0,sizeof(dp)); 14 for(int i=1;i<=n;i++) 15 { 16 int tmp=0; 17 for(int j=1;j<i;j++) 18 { 19 if(a[j]<a[i]) tmp=max(tmp,dp[j]); 20 } 21 dp[i]=max(dp[i],tmp+a[i]); 22 } 23 int ans=0; 24 for(int i=1;i<=n;i++) ans=max(ans,dp[i]); 25 printf("%d\n",ans); 26 } 27 }
F - Piggy-Bank
完全背包问题 只是求一个最小值
1 #include<bits/stdc++.h> 2 using namespace std; 3 int t; 4 int n; 5 int dp[10010]; 6 int w[550]; 7 int v[550]; 8 int cost; 9 const int inf=0x3f3f3f3f; 10 int main() 11 { 12 scanf("%d",&t); 13 while(t--){ 14 int x,y; 15 scanf("%d%d",&x,&y); 16 cost=y-x; 17 scanf("%d",&n); 18 for(int i=0;i<n;i++) 19 scanf("%d%d",&v[i],&w[i]); 20 memset(dp,inf,sizeof(dp)); 21 dp[0]=0; 22 for(int i=0;i<n;i++) 23 { 24 for(int j=w[i];j<=cost;j++) 25 dp[j]=min(dp[j],dp[j-w[i]]+v[i]); 26 } 27 if(dp[cost]==inf) puts("This is impossible."); 28 else printf("The minimum amount of money in the piggy-bank is %d.\n",dp[cost]); 29 } 30 }
H - Tickets
题意是现在要卖n张票 已知 每张票需要的时间 ,另外如果每相邻两张票一起卖也会有一个时间,现在求售票员
最早回家的时间是多少
定义状态dp[i][1]表示第i张票和前一张票一起卖 需要花费时间是多少
dp[i][0]表示 第i张票单独卖出去 需要花费的最小时间
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn=2020; 5 int t; 6 int n; 7 int a[maxn]; 8 int b[maxn]; 9 int dp[maxn][2]; 10 const int inf=0x3f3f3f3f; 11 12 int cal(int i,int j) 13 { 14 return b[i]; 15 } 16 17 int main() 18 { 19 scanf("%d",&t); 20 while(t--){ 21 scanf("%d",&n); 22 for(int i=0;i<n;i++) 23 scanf("%d",&a[i]); 24 for(int i=0;i<n-1;i++) 25 scanf("%d",&b[i]); 26 27 for(int i=0;i<n;i++) 28 { 29 if(i==0) dp[i][0]=a[i],dp[i][1]=inf; 30 else if(i==1) dp[i][0]=min(dp[0][0],dp[0][1])+a[i],dp[i][1]=cal(i-1,i); 31 else { 32 dp[i][0]=min(dp[i-1][0],dp[i-1][1])+a[i]; 33 dp[i][1]=min(dp[i-2][0],dp[i-2][1])+cal(i-1,i); 34 } 35 } 36 int ans=min(dp[n-1][0],dp[n-1][1]); 37 38 int hh=8; 39 int mm=0; 40 int ss=0; 41 hh+=ans/3600;ans%=3600; 42 mm+=ans/60;ans%=60; 43 ss+=ans; 44 printf("%02d:%02d:%02d",hh,mm,ss); 45 if(hh>=12) printf(" pm\n"); 46 else printf(" am\n"); 47 } 48 }
J - FatMouse's Speed
题意是 现在给定 a序列和b序列 现在让你找到一个c序列 使得c作为下标 使得a序列的子序列是递增的 b序列的子序列
是递减的,都为严格
思路是 我们按照a序列进行排序 然后保证a序列递增的基础上 对b序列做最长递减子序列
事实上这里再更新长度的时候 用pre更新一下前驱即可
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn=1010; 5 6 int x,y; 7 struct node 8 { 9 int l; 10 int r; 11 int idx; 12 bool operator <(node u)const 13 { 14 return l==u.l?r>u.r:l<u.l; 15 } 16 }a[maxn]; 17 int dp[maxn]; 18 int pre[maxn]; 19 20 21 int main() 22 { 23 int to=1; 24 int i=1; 25 while(~scanf("%d%d",&x,&y)) 26 { 27 a[to].l=x,a[to].r=y,a[to].idx=i; 28 i++;to++; 29 } 30 sort(a+1,a+1+to); 31 memset(pre,-1,sizeof(pre)); 32 for(int i=1;i<to;i++) 33 { 34 dp[i]=1; 35 for(int j=1;j<i;j++) 36 { 37 if(a[i].l>a[j].l&&a[i].r<a[j].r) 38 { 39 if(dp[i]<dp[j]+1){ 40 pre[i]=j; 41 dp[i]=dp[j]+1; 42 } 43 } 44 } 45 } 46 int pos; 47 int ans=0; 48 for(int i=1;i<to;i++) 49 { 50 if(dp[i]>ans){ 51 ans=dp[i]; 52 pos=i; 53 } 54 } 55 printf("%d\n",ans); 56 stack<int>s; 57 for(;pos!=-1;pos=pre[pos]) 58 { 59 s.push(a[pos].idx); 60 } 61 while(!s.empty()){ 62 printf("%d\n",s.top()); 63 s.pop(); 64 } 65 }
L - Common Subsequence
裸的求两个序列的LCS
1 #include<algorithm> 2 #include<iostream> 3 #include<cstring> 4 #include<stdio.h> 5 using namespace std; 6 7 char a[1000]; 8 char b[1000]; 9 int dp[1000][1000]; 10 int solve(int len1,int len2) 11 { 12 dp[0][0]=dp[0][1]=dp[1][0]=0; 13 for(int i=1;i<=len1;i++) 14 { 15 for(int j=1;j<=len2;j++) 16 { 17 dp[i][j]=max(dp[i-1][j],dp[i][j-1]); 18 if(a[i]==b[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1); 19 } 20 } 21 return dp[len1][len2]; 22 } 23 int main() 24 { 25 26 while(~scanf("%s%s",a+1,b+1)) 27 { 28 int len1=strlen(a+1); 29 int len2=strlen(b+1); 30 printf("%d\n",solve(len1,len2)); 31 } 32 }
N - Longest Ordered Subsequence
直接一个nlogn 求一个LIS
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 5 using namespace std; 6 7 const int inf=0x7fffffff; 8 const int maxn=1010; 9 int a[maxn]; 10 int dp[maxn]; 11 int n; 12 int main() 13 { 14 while(~scanf("%d",&n)) 15 { 16 fill(dp,dp+n,inf); 17 for(int i=1;i<=n;i++) 18 scanf("%d",&a[i]); 19 for(int i=1;i<=n;i++) 20 { 21 int pos=lower_bound(dp,dp+n,a[i])-dp; 22 dp[pos]=a[i]; 23 } 24 int ans=lower_bound(dp,dp+n,inf)-dp; 25 printf("%d\n",ans); 26 } 27 28 }
O - Treats for the Cows
POJ - 3186
题意是 给你一个序列 长度不超过2000,现在开始计时 ,从1 开始 ,每秒你可以从序列的头或者尾部取一个数字,并将这个数字乘以取走的时间 加到总价值里面 ,问最后可以获得的最大价值是多少
比如样例 5
1 3 1 5 2
显然 最后总价值最大值 1*1+2*2+3*3+4*1+5*5 = 43
区间dp,显然每一个子状态都是当前剩余一个区间 为从i到j的一个子区间
定义dp[i][j]为当前剩余区间为i到j时,所能获得的最大价值
显然 dp[i][j]=max( dp[i+1][j]+cnt*a[i] , dp[i][j-1]+cnt*a[j] ),cnt=n-j+i;
1 #include<iostream> 2 #include<cstdio> 3 4 using namespace std; 5 int n; 6 const int maxn=2020; 7 int a[maxn]; 8 int dp[maxn][maxn]; 9 10 int main() 11 { 12 while(~scanf("%d",&n)) 13 { 14 for(int i=0;i<n;i++) scanf("%d",&a[i]); 15 for(int i=n-1;i>=0;i--) 16 { 17 for(int j=i;j<=n-1;j++) 18 { 19 dp[i][j]=max(dp[i+1][j]+a[i]*(i+n-j),dp[i][j-1]+a[j]*(i+n-j)); 20 } 21 } 22 23 printf("%d\n",dp[0][n-1]); 24 } 25 }
P - 记忆化搜索
题意是 给你一个矩阵 每个点都有一个价值 从(0,0)点出发,每到达一个点,就可以获得该点的价值,要求路径必须满足是一个严格升序的 就是下一个到达的位置的价值必须大于当前价值,问最后可以获得的最大的总价值是多少
这题是一个简单的记忆化搜索的过程,从(0,0)出发 每次最多走k步
事实上这里vis是不需要的
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n; 4 int k; 5 int a[110][110]; 6 int dx[]={0,0,1,-1}; 7 int dy[]={-1,1,0,0}; 8 9 int dp[110][110]; 10 int vis[110][110]; 11 int judge(int i,int j) 12 { 13 return i>=0&&i<n&&j>=0&&j<n&&vis[i][j]==0; 14 } 15 16 int dfs(int i,int j) 17 { 18 if(dp[i][j]>=0) return dp[i][j]; 19 int sum=0; 20 vis[i][j]=1; 21 int tmp=0; 22 for(int m=1;m<=k;m++) 23 { 24 for(int n=0;n<4;n++) 25 { 26 int x=dx[n]*m+i; 27 int y=dy[n]*m+j; 28 if(!judge(x,y)) continue; 29 if(a[x][y]>a[i][j]){ 30 vis[x][y]=1; 31 tmp=max(dfs(x,y),tmp); 32 vis[x][y]=0; 33 } 34 } 35 } 36 sum=tmp+a[i][j]; 37 return dp[i][j]=sum; 38 } 39 40 int main() 41 { 42 while(~scanf("%d%d",&n,&k)&&(n!=-1)) 43 { 44 for(int i=0;i<n;i++) 45 { 46 for(int j=0;j<n;j++) 47 scanf("%d",&a[i][j]); 48 } 49 memset(vis,0,sizeof(vis)); 50 memset(dp,-1,sizeof(dp)); 51 dfs(0,0); 52 53 printf("%d\n",dp[0][0]); 54 } 55 }
Q - 最大对称子矩阵
给一个方阵,求矩阵中由左下到右上对称的最大对称子矩阵,
定义 dp[i][j] 为以 (i,j) 为左下角的子矩阵的最大值,
那么很显然,需要再从(i,j)到(i-dp[i-1][j+1],j)中有几个与右边横向相等的 答案就是 dp[i-1][j+1]+k;
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n; 4 char a[1010][1010]; 5 int dp[1010][1010]; 6 7 int judge(int x,int y) 8 { 9 return x>=0&&x<n&&y>=0&&y<n; 10 } 11 12 int main() 13 { 14 while(~scanf("%d",&n)&&n) 15 { 16 int ans=0; 17 for(int i=0;i<n;i++) scanf("%s",a[i]); 18 for(int j=n-1;j>=0;j--) 19 { 20 for(int i=0;i<=n-1;i++) 21 { 22 dp[i][j]=1; 23 int ok=-1; 24 if(i==0||j==n-1) dp[i][j]=1; 25 else{ 26 for(int k=1;k<=dp[i-1][j+1];k++) 27 { 28 if(a[i-k][j]!=a[i][j+k]) { 29 ok=k;break; 30 } 31 } 32 33 if(ok==-1) dp[i][j]=dp[i-1][j+1]+1; 34 else dp[i][j]=ok; 35 } 36 ans=max(dp[i][j],ans); 37 } 38 } 39 40 41 printf("%d\n",ans); 42 } 43 }
R - Milking Time
现在有1000头奶牛需要挤奶 现在给出每头奶牛 开始挤奶时间 结束挤奶时间以及 挤奶结束以后的休息时间,
以及每头奶牛的挤奶数量
现在求能够获得的最大价值是多少
和最长上升子序列类似 现对整体进行一次排序 按照左端点进行排序 然后仿照LIS的做法
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 using namespace std; 5 int n,m,r; 6 const int maxn=1010; 7 struct node 8 { 9 int l,r,w; 10 bool operator < (node u)const 11 { 12 return l==u.l?r<u.r:l<u.l; 13 } 14 }a[maxn]; 15 16 int dp[maxn]; 17 18 int main() 19 { 20 while(~scanf("%d%d%d",&n,&m,&r)) 21 { 22 for(int i=1;i<=m;i++) 23 scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].w); 24 sort(a+1,a+1+m); 25 for(int i=1;i<=m;i++) 26 if(a[i].r<=n) dp[i]=a[i].w; 27 else dp[i]=0; 28 for(int i=1;i<=m;i++) 29 { 30 for(int j=1;j<i;j++) 31 { 32 if(a[j].r+r<=a[i].l&&a[j].r<=n) dp[i]=max(dp[i],dp[j]+a[i].w); 33 } 34 } 35 int ans=-1; 36 for(int i=1;i<=m;i++) 37 ans=max(ans,dp[i]); 38 printf("%d\n",ans); 39 } 40 }
S - Making the Grade
题意是 现在有一个序列 给出n个点 现在把其中的一些值改变 使得这个序列是一个单调增或者是单调减的序列 所需要的代价最小是多少
现在定义单个点的代价是改变后的值与改变前的值的差的绝对值
现在定义dp[i][j]表示 第i个点 高度为j时的最小代价
由于j的范围可能太大 离散化一下即可 由于点最多2000
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 5 using namespace std; 6 7 int n; 8 const int maxn=2020; 9 int a[maxn]; 10 int b[maxn]; 11 int dp[maxn][maxn]; 12 int minn[maxn][maxn]; 13 const int inf=0x3f3f3f3f; 14 int cmp(int i,int j) 15 { 16 return a[i]<a[j]; 17 } 18 19 int main() 20 { 21 while(~scanf("%d",&n)) 22 { 23 for(int i=1;i<=n;i++) 24 scanf("%d",&a[i]); 25 for(int i=1;i<=n;i++) b[i]=i; 26 sort(b+1,b+1+n,cmp); 27 for(int i=0;i<=n;i++) 28 { 29 for(int j=0;j<=n;j++) 30 minn[i][j]=inf; 31 } 32 for(int i=0;i<=n;i++) minn[0][i]=0; 33 for(int i=1;i<=n;i++) 34 { 35 for(int j=1;j<=n;j++) 36 { 37 dp[i][j]=abs(a[b[j]]-a[i])+minn[i-1][j]; 38 minn[i][j]=min(dp[i][j],minn[i][j-1]); 39 } 40 41 } 42 int ans=inf; 43 for(int i=1;i<=n;i++) 44 ans=min(ans,dp[n][i]); 45 printf("%d\n",ans); 46 } 47 48 }