HDU 1024 Max Sum Plus Plus
感觉这题是这个系列里难度最高的题之一?
dp[i][j]代表前j个数找i个区间和,且第j个数在被最后一个区间选中的情况下,的最大和
接着转移是dp[i][j] = max( dp[i][j-1] , max(dp[i-1][k]) ) + a[j], k从i-1到j-1
那么再靠前缀优化+滚动数组就可以ac了
#include<bits/stdc++.h> #include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> #include<map> #include<vector> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 998244353; const int maxn = 1e6 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int a[maxn]; int dp[maxn][2],pre[maxn][2];//pre[i]是从合法位置到i的dp最大值 int main(){ //ios::sync_with_stdio(false); int m,n; while( scanf("%d%d",&m,&n)!=EOF ){ memset(dp,0,sizeof(dp)); memset(pre,0,sizeof(pre)); for(int i=1;i<=n;i++) scanf("%d",a+i); //前i个数找j个区间的最大和 int last=0,now=1;//滚动数组 //初始状态就是i=0的时候,都是dp[0][j]=0,那pre自然也是0 for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++){ if(j<i) continue; if(j==i) { dp[j-1][now]=-inf; pre[j-1][now]=-inf; }//该子问题非法 dp[j][now] = max(dp[j-1][now],pre[j-1][last])+a[j]; pre[j][now] = max( pre[j-1][now],dp[j][now] ); } last = !last; now = !now; } cout<<pre[n][last]<<endl; } return 0; }
HDU 1069 Monkey and Banana
把一个block拆成6个block,然后排序,再求最长下降子序列就行了
#include<bits/stdc++.h> #include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> #include<map> #include<vector> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 998244353; const int maxn = 1e6 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int dp[200];//dp[i]为以第i个cube为结尾的最长下降子序列 struct node{ int l,w,h; node(int l1=0,int w1=0,int h1=0): l(l1),w(w1),h(h1) {} }cubes[200]; bool cmp(node n1,node n2){ if( n1.l==n2.l ) return n1.w>n2.w; return n1.l>n2.l; } int main(){ //ios::sync_with_stdio(false); int n,tc=0; while( scanf("%d",&n) ){ if(n==0) break; for(int i=0;i<n;i++){ int l,w,h; scanf("%d%d%d",&l,&w,&h); cubes[ i*6+1 ] = node(l,w,h); cubes[ i*6+2 ] = node(w,l,h); cubes[ i*6+3 ] = node(l,h,w); cubes[ i*6+4 ] = node(h,l,w); cubes[ i*6+5 ] = node(w,h,l); cubes[ i*6+6 ] = node(h,w,l); } sort(cubes+1,cubes+6*n+1,cmp); cubes[0].l = cubes[0].w = cubes[0].h = inf; //最长递减子序列 memset(dp,0,sizeof(dp)); for(int i=1;i<=6*n;i++){//以i为结尾 for(int j=0;j<i;j++){//枚举倒数第二个数 if( cubes[j].l>cubes[i].l && cubes[j].w>cubes[i].w ) dp[i] = max( dp[i],dp[j]+cubes[i].h ); } } int ans = -inf; for(int i=1;i<=6*n;i++) ans=max(ans,dp[i]); printf("Case %d: maximum height = %d ",++tc,ans); } return 0; }
HDU 1260 Tickets
加个第二维0,1代表当前人是跟上一个一起买的还是单独买的,那就能转移了。
dp[i][0]代表前i个人,在第i个人单独买票的情况下,总最小时间 = min( dp[i-1][0],dp[i-1][1] ) + a[i]
dp[i][1]代表前i个人,在第i个人跟上一人一起买票的情况下,总最小时间= min( dp[i-2][0],dp[i-2][1] ) + b[i]
#include<bits/stdc++.h> #include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> #include<map> #include<vector> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 998244353; const int maxn = 2e3 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int dp[maxn][2]; int a[maxn],d[maxn]; int main(){ //ios::sync_with_stdio(false); int t; cin>>t; while(t--){ int n; cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=2;i<=n;i++) cin>>d[i];//i与i-1一起买票花的时间 for(int i=1;i<=n;i++) dp[i][1] = dp[i][0] = inf; dp[1][0]=a[1]; dp[1][1]=inf; dp[0][0]=0; dp[0][1]=inf; for(int i=2;i<=n;i++){ dp[i][0] = min( dp[i-1][0],dp[i-1][1] ) + a[i]; dp[i][1] = min( dp[i-2][0],dp[i-2][1] ) + d[i]; } int time = min( dp[n][0],dp[n][1] ); //3600秒是1h int h=8+time/3600; time%=3600; //60秒是1分钟 int m=time/60; time%=60; //08:00:08 am if( h<10 ) cout<<"0"<<h<<":"; else cout<<h<<":"; if( m<10 ) cout<<"0"<<m<<":"; else cout<<m<<":"; if(time<10) cout<<"0"<<time<<" "; else cout<<time<<" "; if( h>=12 ) cout<<"pm"<<endl; else cout<<"am"<<endl; } return 0; }
HDU 1257 最少拦截系统
当作模拟做了,如果没有系统能打到那就开新的,如果能打到,那就拿能打的到的最低的打。易证整个导弹系统的拦截高度是个递增序列
#include<bits/stdc++.h> #include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> #include<map> #include<vector> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 998244353; const int maxn = 1e5 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int a[maxn],limit[maxn],cnt;//limit[i]是第i个拦截系统的最大高度 int main(){ //ios::sync_with_stdio(false); int n; while( scanf("%d",&n)!=EOF ){ for(int i=1;i<=n;i++) scanf("%d",a+i); limit[1] = a[1]; //第一个拦截系统打第一个导弹 cnt=1; for(int i=2;i<=n;i++){ bool flag=false;//目前没有系统能击落i导弹 int pick; for(int j=1;j<=cnt;j++){ if( limit[j]>=a[i] ) { flag=true; pick=j; break; } } if( flag ) limit[pick]=a[i]; else limit[++cnt]=a[i]; } printf("%d ",cnt); } return 0; }
POJ 1015 Jury Compromise
三维dp
dp[i][j][k]是前i个人选j个人出来且辩控差为k情况下的辩控和最大值,那么转移方程是,由于k可能被减成负的,所以要加个足够大的常数400
dp[i][j][k] = max( dp[i-1][j][k] , dp[i-1][j-1][ k-(d[i]-p[i]) ] + d[i] + p[i] )
//#include<bits/stdc++.h> #include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> #include<map> #include<vector> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 998244353; const int maxn = 2e2 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int dp[210][25][810],pre[210][25][810]; int d[210],p[210]; int main(){ //ios::sync_with_stdio(false); int n,m,tc=0; while( scanf("%d%d",&n,&m)!=EOF ){ if(n==0 && m==0) break; for(int i=1;i<=n;i++) scanf("%d%d",p+i,d+i); memset(dp,-1,sizeof(dp)); dp[0][0][400]=0; for(int i=1;i<=n;i++){ for(int k=0;k<=800;k++) dp[i][0][k]=-1;//-1代表问题根本不合法 dp[i][0][400]=0; for(int j=1;j<=min(i,m);j++){ for(int k=0;k<=800;k++){//枚举当前的辩控差 int cha = p[i]-d[i];//第i个人带来的辩控差 if( k-cha<0 || k-cha>800 ) continue; if( dp[i-1][j][k]==-1 && dp[i-1][j-1][k-cha]!=-1 ) { dp[i][j][k] = dp[i-1][j-1][k-cha]+d[i]+p[i]; pre[i][j][k]=1; } else if( dp[i-1][j][k]!=-1 && dp[i-1][j-1][k-cha]==-1 ) { dp[i][j][k] = dp[i-1][j][k]; pre[i][j][k]=0; } else if( dp[i-1][j][k]==-1 && dp[i-1][j-1][k-cha]==-1 ) continue; else{ if( dp[i-1][j][k]>dp[i-1][j-1][k-cha]+d[i]+p[i] ) { dp[i][j][k] = dp[i-1][j][k]; pre[i][j][k]=0; } else { dp[i][j][k] = dp[i-1][j-1][k-cha]+d[i]+p[i]; pre[i][j][k]=1; } } // cout<<i<<" "<<j<<" "<<k<<" "<<dp[i][j][k]<<endl; } } } int cnt1=0,cnt2=0; int n1=n,m1=m,k1=-1; if(dp[n][m][400]!=-1) k1=400; else{ for(int i=1;i<=400;i++){ if( dp[n][m][400+i]!=-1 ) k1=400+i; if( dp[n][m][400-i]!=-1 ){ if( k1==-1) k1=400-i; else if( dp[n][m][400-i]>dp[n][m][k1] ) k1=400-i; } if(k1!=-1) break; } } vector<int> ans; while(n1){ //cout<<n1<<" "<<m1<<" "<<k1<<" "<<pre[n1][m1][k1]<<" "<<p[n1]<<" "<<d[n1]<<endl; if( pre[n1][m1][k1] ) { ans.push_back(n1); cnt1+=p[n1]; cnt2+=d[n1]; k1-=(p[n1]-d[n1]); n1--; m1--; } else n1--; } if(tc) printf(" "); printf("Jury #%d ",++tc); printf("Best jury has value %d for prosecution and value %d for defence: ",cnt1,cnt2); for(int i=ans.size()-1;i>=0;i--) printf(" %d",ans[i]); } return 0; }
POJ 1661 Help Jimmy
打个岔,这题可以当最短路做,但并没有看出来
dp[i][0]是从第i个平台的左边往下掉到地面的最小时间 = min( dp[k][0] + 掉落时间 + 从摔落点跑到k平台左端点时间 , dp[k][1] + 掉落时间 + 从摔落点跑到k平台右端点时间 ) k是掉下去以后掉到的那个平台
dp[i][1]就略了
#include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> #include<map> #include<vector> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 998244353; const int maxn = 1e3 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int dp[maxn][2]; struct node{ int l,r,h; node(int l1=0,int r1=0,int h1=0): l(l1),r(r1),h(h1) {} }plats[maxn]; bool cmp(node n1,node n2){ if( n1.h==n2.h ) return n1.l<n2.l; return n1.h<n2.h; } int main(){ //ios::sync_with_stdio(false); int t; cin>>t; while(t--){ int n,x,y,maxh; cin>>n>>x>>y>>maxh; for(int i=1;i<=n+2;i++) dp[i][0]=dp[i][1]=inf; dp[1][1] = dp[1][0] = 0; for(int i=1;i<=n;i++){ int l,r,h; cin>>l>>r>>h; plats[i] = node(l,r,h); } plats[n+1]=node(-inf,inf,0); plats[n+2]=node(x,x,y); //for(int i=1;i<=n+2;i++) cout<<plats[i].l<<" "<<plats[i].r<<" "<<plats[i].h<<endl; cout<<endl; sort(plats+1,plats+1+n+2,cmp); //for(int i=1;i<=n+2;i++) cout<<plats[i].l<<" "<<plats[i].r<<" "<<plats[i].h<<endl; for(int i=2;i<=n+2;i++){ for(int j=i-1;j>=1;j--){//往左 if( plats[i].l>=plats[j].l && plats[i].l<=plats[j].r && plats[i].h-plats[j].h<=maxh ){ if( j==1 ) dp[i][0]=plats[i].h-plats[j].h; else{ int x1=dp[j][0]+ plats[i].l-plats[j].l,x2=dp[j][1]+ plats[j].r-plats[i].l; int plus = min(x1,x2); dp[i][0] = plats[i].h-plats[j].h + plus; //cout<<plats[i].h<<" "<<plats[j].h<<" "<<x1<<" "<<x2<<endl; break; } } } for(int j=i-1;j>=1;j--){ if( plats[i].r>=plats[j].l && plats[i].r<=plats[j].r && plats[i].h-plats[j].h<=maxh ){ if(j==1) dp[i][1] = plats[i].h-plats[j].h; else{ int x1=dp[j][0]+ plats[i].r-plats[j].l,x2=dp[j][1]+ plats[j].r-plats[i].r; int plus = min(x1,x2); dp[i][1] = plats[i].h-plats[j].h + plus; break; } } } // cout<<i<<" "<<dp[i][0]<<" "<<dp[i][1]<<endl; } cout<<dp[n+2][0]<<endl; } return 0; }
POJ 3186 Treats for the Cows
状态定义比较难想,想到就好做了
dp[i][j]为从i卖到j的最大价值(此最大价值指的是以1-n为两端的最大值,而不是以i-j为两端)
当卖到还剩i-j的时候,这时候再卖i或j是在第n-j+i天卖,所以
dp[i][j] = max( dp[i+1][j] + (n-j+i)*value[i] , dp[i][j-1]+(n-j+i)*value[j] )
base case是i==j的时候,拿递归会比较好写
//#include<bits/stdc++.h> #include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> #include<map> #include<vector> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 998244353; const int maxn = 2e3 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int memo[maxn][maxn],value[maxn],n; int dp(int i,int j){ if( memo[i][j]!=-1 ) return memo[i][j]; if( i==j ) return n*value[i]; return memo[i][j] = max( dp(i+1,j)+value[i]*(n-j+i), dp(i,j-1)+value[j]*(n-j+i) ); } int main(){ //ios::sync_with_stdio(false); while( scanf("%d",&n)!=EOF ){ memset(memo,-1,sizeof(memo)); for(int i=1;i<=n;i++) cin>>value[i]; printf("%d ",dp(1,n)); } return 0; }
HDU 2859 Phalanx
dp[i][j]为以(i,j)为矩阵最左下点的最大对称矩阵
那么按照对称的要求,(i,j)右侧点应与其对应的上侧点相同,我们设有len个相同
那么dp[i][j] = dp[i-1][j+1] + 1 如果 len>=dp[i-1][j+1]
else dp[i][j] = len+1,想一下就好了
#include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> #include<map> #include<vector> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 998244353; const int maxn = 1e3 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; char maze[maxn][maxn]; int dp[maxn][maxn]; int main(){ int n; while( scanf("%d",&n)!=EOF ){ if(n==0) break; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) cin>>maze[i][j]; for(int i=1;i<=n;i++) dp[1][i] = dp[i][n] = 1; for(int i=2;i<=n;i++){ for(int j=1;j<n;j++){ int len=0; //上侧与右侧比 for(int k=1;k<=n;k++){ if( i-k<1 || j+k>n ) break; if( maze[i-k][j]!=maze[i][j+k] ) break; len++; } if( len>=dp[i-1][j+1] ) dp[i][j]=dp[i-1][j+1]+1; else dp[i][j] = len+1; } } int ans=1; for(int i=2;i<=n;i++) for(int j=1;j<n;j++) ans=max(ans,dp[i][j]); cout<<ans<<endl; } return 0; }
POJ 3616 Milking Time
先排序,然后dp[i]代表在i时间段挤奶的最大挤奶量,那么
dp[i] = max( dp[k] + i时间段的efficiency ) k为所有在i之前且不与i冲突的时间段
#include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> #include<map> #include<vector> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 998244353; const int maxn = 1e3 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; int rest,dp[maxn];//dp[i]为在第i个时间段的最大挤奶量 struct node{ int l,r,value; node(int l1=0,int r1=0,int v1=0): l(l1),r(r1),value(v1) {} }milking[maxn]; bool cmp(node n1,node n2){ return n1.r<n2.r; } int main(){ int n,m; while( scanf("%d%d%d",&n,&m,&rest)!=EOF ){ for(int i=1;i<=m;i++){ int l,r,value; scanf("%d%d%d",&l,&r,&value); milking[i] = node(l,r,value); } sort(milking+1,milking+1+m,cmp); for(int i=1;i<=m;i++) dp[i]=milking[i].value; //for(int i=1;i<=m;i++) cout<<milking[i].l<<" "<<milking[i].r<<" "<<milking[i].value<<endl; int ans=0; for(int i=1;i<=m;i++){ for(int j=1;j<i;j++){ if( milking[j].r+rest<=milking[i].l ) dp[i] = max( dp[i],dp[j]+milking[i].value ); ans=max(ans,dp[i]); } } cout<<ans<<endl; } return 0; }
POJ 3666 Making the Grade
dp[i][j]代表前i个坡,且第i个坡的高度为j的情况下,的最小代价
dp[i][j] = min( dp[i-1][k] ) + abs(j-a[i]) k为1-j(所有小于等于j的高度)
所以可以利用前缀优化掉j复杂度的时间,但是j的高度可能很高,所以大胆贪心猜个结论最终的土坡高度一定源于a数组里的某个数,所以离散化做一下就ok了
//#include<bits/stdc++.h> #include<stdio.h> #include<iostream> #include<algorithm> #include<cstring> #include<map> #include<vector> #define inf 2e9 #define maxnode 200000 #define ll long long #define lowbit(x) (x&(-x)) const int mod = 998244353; const int maxn = 2e3 + 10; int dx[4]={0,0,1,-1}; int dy[4]={1,-1,0,0}; using namespace std; ll dp[maxn][maxn]; int a[maxn],b[maxn]; int main(){ int n; cin>>n; for(int i=1;i<=n;i++) { cin>>a[i]; b[i]=a[i]; } sort(b+1,b+1+n); for(int i=1;i<=n;i++){//从第1个土堆到第i个土堆 ll m=dp[i-1][1]; for(int j=1;j<=n;j++){//第i个土堆的高度为j时的最小cost m=min(m,dp[i-1][j]); dp[i][j] = abs( a[i]-b[j] ) + m; } } ll ans=inf; for(int i=1;i<=n;i++) ans=min(ans,dp[n][i]); cout<<ans<<endl; return 0; }