kuangbin专题十二 基础DP
kuangbin专题十二 基础DP
- A - HDU1024 Max Sum Plus Plus
- B - HDU1029 Ignatius and the Princess IV
- C - HDU1069 Monkey and Banana
- D - HDU1074 Doing Homework
- E - Hdu1087 Super Jumping! Jumping! Jumping!
- F - Hdu1114 Piggy-Bank
- G - HDU1176 免费馅饼
- H - HDU1260 Tickets
- I - Hdu1257 最少拦截系统
- J - HDU1160 FatMouse's Speed
- K - [POJ1015](http://poj.org/problem?id=1015) Jury Compromise
- L - [POJ1458](http://poj.org/problem?id=1458) Common Subsequence
- M - [POJ1661](http://poj.org/problem?id=1661) Help Jimmy
- N - [POJ2533](http://poj.org/problem?id=2533) Longest Ordered Subsequence
- O - [POJ3186](http://poj.org/problem?id=3186) Treats for the Cows
- P - HDU1078 FatMouse and Cheese
- Q - HDU285 Phalanx
- R - [POJ3616](http://poj.org/problem?id=3616) Milking Time
- S - [POJ3666](http://poj.org/problem?id=3666) Making the Grade
A - HDU1024 Max Sum Plus Plus
一维code:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e6+10,inf=0x3f3f3f3f;
int pre[N],f[N],a[N],m,n;int main()
{while(~scanf("%d %d",&m,&n)){for(int i=1;i<=n;i++) scanf("%d",&a[i]);memset(pre,0,sizeof(pre));memset(f,0,sizeof(f));int Max;for(int i=1;i<=m;i++){Max=-inf;for(int j=i;j<=n;j++){f[j]=max(f[j-1],pre[j-1])+a[j];pre[j-1]=Max;Max=max(Max,f[j]);}}printf("%d\n",Max);}return 0;
}
二维code(慎用):
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e6+10,inf=0x3f3f3f3f;
int f[100][N],a[N],m,n;int main()
{while(~scanf("%d %d",&m,&n)){for(int i=1;i<=n;i++) scanf("%d",&a[i]);memset(f,0,sizeof(f));int Max;for(int i=1;i<=m;i++){Max=-inf;for(int j=i;j<=n;j++){f[i][j]=f[i-1][j-1];f[i][j]=max(f[i][j],f[i][j-1])+a[j];f[i][j-1]=Max; Max=max(Max,f[i][j]); }}printf("%d\n",Max);}return 0;
}
题意:给你n个数,让你求m个子段和的最大值,子段之间不能相重合。
思路:这道题一开始想的时候我们可以定义f[i][j]:前j个数中当前i个子段和的最大值,那么状态转移方程为:f[i][j]=max(f[i-1][j-1],f[i][j-1])+a[j],但其实这样做空间可能会超限(因为现在HDU进不去,我不好判断),网上找的题解用的都是一维的,所以我就降维。仔细想想,上一层的答案我们可以用一个数组pre记录下来,这样我们就可以做到用一维来解决了。f[j]:当前层数的前i个数的子段和的最大值,那么状态转移方程为f[j]=max(f[j-1],pre[j-1])+a[j]。另外有一点要注意一下:pre[j-1]=Max这个式子一定要放在上面两个式子之间,因为你要先取完当前下标的最大值Max后再更新当前下标pre的值,不然肯定会出错的!
B - HDU1029 Ignatius and the Princess IV
code:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=1e6+10;
int n,a[N];
int main()
{while(cin>>n){memset(a,0,sizeof(a));int flag=0;for(int i=1;i<=n;i++){int c;cin>>c;a[c]++;if(a[c]>=((n+1)/2)&&flag==0) {cout<<c<<"\n";flag=1;continue;}}}return 0;
}
水题,不过多解释。
C - HDU1069 Monkey and Banana
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=200;
struct Node{int x,y,z;
}block[200];
int t,n,f[N];
bool cmp(Node a,Node b)
{if(a.x!=b.x) return a.x<b.x;else return a.y<b.y;
}
int main()
{t=1;while(cin>>n){if(n==0) break;for(int i=0;i<n;i++){int a,b,c;cin>>a>>b>>c;block[6*i].x=a,block[6*i].y=b,block[6*i].z=c;block[6*i+1].x=a,block[6*i+1].y=c,block[6*i+1].z=b;block[6*i+2].x=b,block[6*i+2].y=a,block[6*i+2].z=c;block[6*i+3].x=b,block[6*i+3].y=c,block[6*i+3].z=a;block[6*i+4].x=c,block[6*i+4].y=a,block[6*i+4].z=b;block[6*i+5].x=c,block[6*i+5].y=b,block[6*i+5].z=a;}sort(block,block+6*n,cmp);for(int i=0;i<6*n;i++){f[i]=block[i].z;for(int j=0;j<i;j++){if(block[i].x>block[j].x&&block[i].y>block[j].y)f[i]=max(f[i],f[j]+block[i].z); }}int maxn=-1;for(int i=0;i<6*n;i++){if(maxn<f[i])maxn=f[i];}printf("Case %d: maximum height = %d\n",t++,maxn);}return 0;
}
题意:有n种类型的矩形方块,每个方块可用无限多个,问最多能叠多高,叠加的条件是上面的方块的长和宽要严格小于下面方块的长和宽。
思路:这道题有点像LIS问题,我们可以把长宽高任意组合,一种类型有6种组合,那么n种类型有6n种组合,之后把这6n种组合存在结构体数组内,按照长度相同时宽度从小到大,长度不相同时长度从小到大排序。状态转移方程为:f[i]=max(f[i],f[j]+block[i].z),不过要加一个判断条件:block[i].x>block[j].x&&block[i].y>block[j].y,然后在6*n种答案里选最大值即可。
D - HDU1074 Doing Homework
code:
#include<iostream>
#include<cstdio>
using namespace std;
const int N=20,inf=0x3f3f3f3f;
struct Node{string name;int deadline;int day;
}course[N];
struct Dp{int now;存放当前的时间int pre;//存放前一个状态到达当前状态所完成的课程int score;//存放当前减少的分数
}dp[1 << N];
void output(int x)
{int l=0;int ans[N];while(x){ans[l++]=dp[x].pre;x=x-(1 << dp[x].pre);}for(int i=l-1;i>=0;i--)cout<<course[ans[i]].name<<"\n";
}
int main()
{int t,n;scanf("%d",&t);while(t--){scanf("%d",&n);for(int i=0;i<n;i++){cin >> course[i].name >> course[i].deadline >> course[i].day;}dp[0].now=0;dp[0].pre=-1;dp[0].score=0;for(int i=1;i<(1 << n);i++)dp[i].score = inf;for(int i=0;i<(1 << n);i++)//枚举所有状态{for(int j=0;j<=n-1;j++){int tmp=(1 << j);if(i & tmp) continue;//如果第j门课程已经做了int t=i | tmp;//加入第j门课程,到达当前状态int time=dp[i].now+course[j].day;//计算当前时间int sc=0;if(time>course[j].deadline)//如果时间超了就计算要被减去的分数sc=time-course[j].deadline;sc+=dp[i].score;//跟上一个状态的分数相加if(dp[t].score>sc)//看是否能更新{dp[t].now=time;dp[t].pre=j;dp[t].score=sc;}}}cout<<dp[(1 << n)-1].score<<"\n";//找到所有功课都完成的状态的结果output((1 << n)-1);}return 0;
}
题意:给你n门课程的名字、截止时间和做完这门课程需要花的时间,课程没有在规定时间内做完会扣分,求一种方案使得扣分最少,输出最少扣分和方案。
思路:参考了这位博主的代码再加了些自己的看法。这道题我们可以先看数据范围,1<=N<=15,这就提醒我们可以用状态压缩DP来写。1代表写了,0代表没写,用二进制表示课程完成情况,我们可以规定1011代表第1、2、4三门课程完成了,第3门课程没完成,也可以规定1011代表第1,3,4三门课程完成了,第2门课程没完成,但在这里你只能选前者,因为题目中规定了字母序最小的,如果你要按后面的来写,我认为是写不出来的,答案一定会错!还有一点要注意,博主的代码循环跟我的代码循环刚好相反:for(int j=n-1;j>=0;j–),在这里循环的顺序不会影响最终结果,这是我思考了很长时间得出的结论,影响最终结果的是你对1011的规定是前者还是后者。
E - Hdu1087 Super Jumping! Jumping! Jumping!
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1005;
ll ans,dp[N],a[N],n;
int main()
{while(cin>>n){if(n==0) break;for(int i=1;i<=n;i++)scanf("%d",&a[i]);ans=-1;for(int i=1;i<=n;i++){dp[i]=a[i];for(int j=1;j<=i;j++){if(a[i]>a[j]){dp[i]=max(dp[j]+a[i],dp[i]);}}ans=max(ans,dp[i]);}cout<<ans<<"\n";}return 0;
}
题意:就是找一个上升子序列,使他的和最大。
思路:可以类比求最长上升子序列的题来写。
F - Hdu1114 Piggy-Bank
code:
#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int t;
int e,f,n,v[505],w[505],dp[10005];
int main()
{scanf("%d",&t);while(t--){cin>>e>>f>>n;for(int i=1;i<=n;i++)scanf("%d %d",&v[i],&w[i]);dp[0]=0;for(int i=1;i<=f-e;i++) dp[i]=0x3f3f3f3f;for(int i=1;i<=n;i++)for(int j=w[i];j<=f-e;j++){dp[j]=min(dp[j],dp[j-w[i]]+v[i]);}if(dp[f-e]!=0x3f3f3f3f)cout<<"The minimum amount of money in the piggy-bank is "<<dp[f-e]<<"."<<endl;else cout<<"This is impossible."<<endl;}return 0;
}
题意:给定一个重量为f-e的背包和n种类型的物品,每个物品有重量和价值,每种物品有无限多个,问背包恰好放满且背包里物品总价值最低为多少。
思路:基本跟完全背包问题相同,就是要把max改为min,其他代码相同。
G - HDU1176 免费馅饼
1.code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,dp[100005][20];
int main()
{while(~scanf("%d",&n)){if(n==0) break;int maxt=-1;memset(dp,0,sizeof(dp));for(int i=0;i<n;i++){int t,p;scanf("%d%d",&p,&t);dp[t][p]++;maxt=max(maxt,t);}for(int i=maxt;i>=0;i--){for(int j=0;j<11;j++){dp[i][j]+=max(dp[i+1][j],max(dp[i+1][j-1],dp[i+1][j+1]));}}printf("%d\n",dp[0][5]);}return 0;
}
2.code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,dp[100005][20];
int main()
{while(~scanf("%d",&n)){if(n==0) break;int maxt=-1;memset(dp,0,sizeof(dp));for(int i=0;i<n;i++){int t,p;scanf("%d%d",&p,&t);dp[t][p]++;maxt=max(maxt,t);}for(int i=1;i<=maxt;i++){for(int j=0;j<11;j++){if(j>=1)dp[i][j]+=max(dp[i-1][j],max(dp[i-1][j-1],dp[i-1][j+1]));else dp[i][j]+=max(dp[i-1][j],dp[i-1][j+1]);}}int ans=-1;for(int j=0;j<11;j++)ans=max(ans,dp[maxt][j]);printf("%d\n",ans);}return 0;
}
题意:一个人在0~10这11个位置捡馅饼,一开始他在5这个位置,第1s时他能捡4,5,6这3个位置的馅饼中的一种。馅饼会在T时间掉落在x这个位置,给你n个馅饼的x和T,问最多能捡几个馅饼。馅饼可能会在同一时间同一位置掉落多个馅饼。
思路:定义dp[i][j]:截止到第j秒i位置捡到的馅饼数量的最大值,我们有两种思路,你可以从前往后算,也可以从后往前算(时间上),如果是从后往前算,就是第一种代码,答案就是dp[0][5]了,反之就是第二种代码,要在最大时间的0~10这11个位置取最大值。
H - HDU1260 Tickets
code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=2005;
int s[N],d[N],dp[N];
int main()
{int n;cin>>n;while(n--){int k;cin>>k;memset(dp,0,sizeof(dp));for(int i=1;i<=k;i++)cin>>s[i];for(int i=2;i<=k;i++)cin>>d[i];d[1]=s[1];for(int i=2;i<=k;i++){dp[i]=min(dp[i-1]+s[i],dp[i-2]+d[i]);}int ss=d[k]%60;int m=d[k]/60%60;int h=d[k]/3600;h+=8;if(h<=12)printf("%02d:%02d:%02d am\n",h,m,ss);else printf("%02d:%02d:%02d pm\n",h-12,m,ss);}return 0;
}
题意:给你N个测试样例,每个样例会给出k个人买票所需的时间以及k-1个两个相邻的人买票总共需要的时间,问这k个人买票所需的最少时间。
思路:线性DP,dp[i]表示第1个人到第i个人买票花的最少时间,那么状态转移方程为:dp[i]=min(dp[i-1]+s[i],dp[i-2]+d[i]),dp[k]就是答案,然后根据样例输出即可。
I - Hdu1257 最少拦截系统
code:
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int a[N],dp[N];
int main()
{int n;while(cin>>n){for(int i=0;i<n;i++) {cin>>a[i];dp[i]=1;}for(int i=0;i<n;i++){for(int j=0;j<i;j++){if(a[j]<a[i]){dp[i]=max(dp[i],dp[j]+1); }}}int ans=0;for(int i=0;i<n;i++)ans=max(ans,dp[i]);cout<<ans<<"\n";}return 0;
}
题意:不解释,看题目,很直白。
思路:这道题一开始我以为是求多次最长下降子序列,每次求完就删掉,直到全部删完,输出次数即可,但后来发现这样做不太现实,做不出来。想过一个思路,发现如果后面导弹的高度比前面的高,就要换过一个导弹系统,于是我们就可以按照最长上升子序列的代码来写即可得出答案。
J - HDU1160 FatMouse’s Speed
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct Node{int w,s,id;
}m[1005];
struct DP{int num,pre;
}dp[1005];
bool cmp(Node a,Node b)
{if(a.w!=b.w) return a.w<b.w;
}
void output_path(int x)
{if(dp[x].pre!=-1)output_path(dp[x].pre);printf("%d\n",m[x].id);
}
int main()
{int t=1;while(~scanf("%d %d",&m[t].w,&m[t].s)){m[t].id=t;++t;}sort(m+1,m+1+t,cmp);int ans=0,x=-1;for(int i=1;i<=t;i++)dp[i].num=1,dp[i].pre=-1;for(int i=1;i<=t;i++){for(int j=1;j<i;j++){if((m[j].w<m[i].w)&&(m[j].s>m[i].s)){if(dp[i].num<dp[j].num+1){dp[i].num=dp[j].num+1;dp[i].pre=j;} }}if(ans<dp[i].num)ans=dp[i].num,x=i;}printf("%d\n",ans);output_path(x);return 0;
}
题意:给你一定数量的老鼠,每个老鼠有体重和速度两个属性,需要求一个老鼠序列,使得这个序列的长度最长,输出序列的长度和路径。序列要符合老鼠的体重越大,速度越小的结论。
思路:这道题一开始不知道怎么输出路径,看了题解之后才恍然大悟。就是要在dp这个结构体数组中加一个pre这个变量来记录路径,还要记录序列最长的最后一个元素的下标就可以解决了。我的做法是用一个结构体数组m来记录体重,速度和下标,然后对这个数组按照体重从小到大排序,然后在循环中进行比较,更新答案,输出路径用一个递归函数来解决。
K - POJ1015 Jury Compromise
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
int dp[21][805];
//现用dp(j, k)表示,取j 个候选人,使其辩控差为k 的所有方案中,
//辩控和最大的那个方案(该方案称为“方案f(j, k)”)的辩控和
vector<int> path[21][805];//记录路径 path[i][j] 取i个人 他们的差为j时候的路径
int main()
{int n,m,t=1;while(~scanf("%d %d",&n,&m)){if(n==0&&m==0) break;int sub[210],_plus[210];for(int i=0;i<n;i++){int d,p;scanf("%d %d",&d,&p);sub[i]=d-p;_plus[i]=d+p;}for(int i=0;i<m;i++){for(int j=0;j<805;j++){path[i][j].clear();}}memset(dp,-1,sizeof(dp));int fix=20*m;dp[0][fix]=0;for(int k=0;k<n;k++){for(int i=m-1;i>=0;i--){for(int j=0;j<2*fix;j++){if(dp[i][j]>=0){if((dp[i+1][j+sub[k]])<=(dp[i][j]+_plus[k])){dp[i+1][j+sub[k]]=dp[i][j]+_plus[k];path[i+1][j+sub[k]]=path[i][j];path[i+1][j+sub[k]].push_back(k);}}}}}int i;for(i=0;dp[m][fix+i]==-1&&dp[m][fix-i]==-1;i++);int temp = (dp[m][fix+i] > dp[m][fix-i]) ? i : -i;int sumD=(dp[m][fix+temp]+temp)/2;int sumP=(dp[m][fix+temp]-temp)/2;printf("Jury #%d\n",t++);printf("Best jury has value %d for prosecution and value %d for defence:\n",sumD,sumP);for(int i=0;i<m;i++)printf(" %d",path[m][fix+temp][i]+1);printf("\n\n");}return 0;
}
L - POJ1458 Common Subsequence
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=1e3+10;
int dp[N][N];
int main()
{char a[N],b[N];while(~scanf("%s %s",a,b)){int n=strlen(a);int m=strlen(b);memset(dp,0,sizeof(dp));for(int i=0;i<n;i++){for(int j=0;j<m;j++){if(a[i]==b[j])dp[i+1][j+1]=dp[i][j]+1;else dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]);}}printf("%d\n",dp[n][m]);}return 0;
}
M - POJ1661 Help Jimmy
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1005,INF=0x3f3f3f3f;
int dp[N][5];
int n,x,y,Max;
struct Plat{int x1,x2,high;
}plat[N];
bool cmp(Plat a,Plat b)
{return a.high<b.high;
}
void LeftMove(int i)
{int k=i-1;while((k>0)&&(plat[i].high-plat[k].high<=Max)){if((plat[i].x1>=plat[k].x1)&&(plat[i].x1<=plat[k].x2)){dp[i][0]=plat[i].high-plat[k].high+min(plat[i].x1-plat[k].x1+dp[k][0],plat[k].x2-plat[i].x1+dp[k][1]);return;}else --k;}if(plat[i].high-plat[k].high>Max)dp[i][0]=INF;else dp[i][0]=plat[i].high;
}
void RightMove(int i)
{int k=i-1;while((k>0)&&(plat[i].high-plat[k].high<=Max)){if((plat[i].x2>=plat[k].x1)&&(plat[i].x2<=plat[k].x2)){dp[i][1]=plat[i].high-plat[k].high+min(plat[i].x2-plat[k].x1+dp[k][0],plat[k].x2-plat[i].x2+dp[k][1]);return;}else --k;}if(plat[i].high-plat[k].high>Max)dp[i][1]=INF;else dp[i][1]=plat[i].high;
}
int ShortestTime()
{for(int i=1;i<=n+1;i++){LeftMove(i);RightMove(i);}return min(dp[n+1][0],dp[n+1][1]);
}
int main()
{int t;scanf("%d",&t);while(t--){scanf("%d%d%d%d",&n,&x,&y,&Max);memset(dp,0,sizeof(dp));for(int i=1;i<=n;i++){scanf("%d%d%d",&plat[i].x1,&plat[i].x2,&plat[i].high);}plat[n+1].x1=x;plat[n+1].x2=x;plat[n+1].high=y;plat[0].x1=-20000;plat[0].x2=20000;plat[0].high=0;sort(plat,plat+n+2,cmp);printf("%d\n",ShortestTime());}return 0;
}
N - POJ2533 Longest Ordered Subsequence
#include<iostream>
#include<algorithm>
using namespace std;
int dp[1005],a[1005];
int main()
{int n,ans=-1;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);}for(int i=1;i<=n;i++){dp[i]=1;for(int j=1;j<i;j++){if(a[i]>a[j]){dp[i]=max(dp[i],dp[j]+1);}}ans=max(ans,dp[i]);}printf("%d",ans);return 0;
}
O - POJ3186 Treats for the Cows
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2005;
int a[N],dp[N][N];int main()
{int n;scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);for(int len=1;len<=n;len++){for(int i=1,j=i+len-1;i<=n;i++,j++){dp[i][j]=max(dp[i+1][j]+a[i]*(n-len+1),dp[i][j-1]+a[j]*(n-len+1));}}printf("%d",dp[1][n]);return 0;
}
题意:双端队列里取数,每次取数都乘上它取出时的序列号,问和最大为多少。
思路:dp[i][j]表示序列从i~j的所求值。经典区间DP,不过是倒着推,难点在于当前区间天数的表示。
P - HDU1078 FatMouse and Cheese
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int a[105][105];
bool vis[105][105];
int ans[105][105],dx[]={1,-1,0,0},dy[]={0,0,1,-1};
int n,k;
void dfs(int x,int y)
{for(int i=0;i<4;i++){for(int j=1;j<=k;j++){int xx=x+dx[i]*j;int yy=y+dy[i]*j;if(a[x][y]<a[xx][yy]&&vis[xx][yy]==0&&xx>=0&&xx<n&&yy>=0&&yy<n){vis[xx][yy]=1;ans[xx][yy]=max(ans[xx][yy],ans[x][y]+a[xx][yy]);dfs(xx,yy);vis[xx][yy]=0;}}}
}
int main()
{while(~scanf("%d%d",&n,&k)){if(n==-1&&k==-1) return 0;memset(vis,0,sizeof(vis));for(int i=0;i<n;i++)for(int j=0;j<n;j++){scanf("%d",&a[i][j]);ans[i][j]=a[i][j];}dfs(0,0);int anss=-1;for(int i=0;i<n;i++)for(int j=0;j<n;j++){anss=max(anss,ans[i][j]);}printf("%d\n",anss);}return 0;
}
题意:老鼠一开始在(0,0)的位置,每个位置都有一定数量的食物,老鼠一次最多走k步,规定走过的位置的食物量要越来越大,问老鼠最多能吃的食物量。
思路:挺简单的,记忆化搜索DP。
Q - HDU285 Phalanx
code:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=1005;
char st[maxn][maxn];
int dp[maxn][maxn];
int main()
{int n,ans,t1,t2;while(scanf("%d",&n) && n){ans=0;for(int i=0;i<n;i++)scanf("%s",st[i]);for(int i=0;i<n;i++){for(int j=0;j<n;j++){if(i==0||j==n-1)dp[i][j]=1;else{t1=i,t2=j;while(t1>=0&&t2<n){if(st[t1][j]==st[i][t2])t1--,t2++;else break;}int t=i-t1;dp[i][j] = min(i-t1, dp[i-1][j+1]+1);}ans=max(ans,dp[i][j]);}}printf("%d\n",ans);}return 0;
}
题意:给定一个n行n列的字符矩阵。求这个矩阵的最大对称子矩阵的大小。在这里的对称指的是关于左下和右上相连的对角线对称。
思路:我们定义dp[i][j]:0~i, j~n-1形成的矩阵的最大对称子矩阵的大小。
我们可以先将dp[i][j]全部初始化为1,也可以像上面代码一样只初始化第一行和第n-1列为1,后面的具体操作可结合代码和图片来理解。
R - POJ3616 Milking Time
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct Node{int l,r,e;
}a[1005];
int f[1005];
bool cmp(Node a,Node b)
{return a.r<b.r;
}
int main()
{int N,M,R;scanf("%d%d%d",&N,&M,&R);for(int i=0;i<M;i++){scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].e);a[i].r+=R;//预处理}sort(a,a+M,cmp);//按右端点从小到大排序int k;k=0;for(int i=1;i<=N+R;i++){f[i]=f[i-1];while(a[k].r==i) f[i]=max(f[i],f[a[k].l]+a[k++].e);//可能有多个值相等,要用while}printf("%d",f[N+R]);return 0;
}
题意:给了M个时间段和产奶量,要在规定的N个小时内分配合理时间使总产奶量最大。注意奶牛产完奶后要有R个小时休息后才能继续工作。
S - POJ3666 Making the Grade
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2005,INF=0x3f3f3f3f;
int dp[N][N],a[N],b[N],n;
void solve()
{for(int i=1;i<=n;i++){int minn=INF;for(int j=1;j<=n;j++){int cost=abs(a[i]-b[j]);minn=min(minn,dp[i-1][j]);//dp[i-1][j]指的是前i-1个数最大值为b[j]时的最小代价dp[i][j]=cost+minn;}}int ans=INF;for(int i=1;i<=n;i++)ans=min(ans,dp[n][i]);printf("%d",ans);
}
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d",&a[i]);b[i]=a[i];}sort(b+1,b+1+n);solve();return 0;
}
这道题一开始我没啥思路,后来看了题解才知道是线性DP+离散化。可以参考这位博主的思路。
题意:给出一个长度为 n 的序列,要求使序列变为非递增或非递减序列,问花费的最少代价。在之后的做题中统一将其改为非递减序列。原因后面会解释。
设 dp[i][j] 为长度为前 i 个数构成的序列,且处理完最大值为 j 所花费的相应代价,那么可以得出状态转移方程:
注意到 j 最大可达到 1,000,000,000,那么显然枚举的话一定会 TLE,而 n 的大小最大只有 2000,那么使用离散化的思想,先对序列 a[i] 进行处理,即:
kuangbin专题十二 基础DP相关推荐
- [kuangbin带你飞]专题十二 基础DP1 题解+总结
kuangbin带你飞:点击进入新世界 总结: 简单dp,最近在做,持续更新. 文章目录 总结: 1.Max Sum Plus Plus 2.Ignatius and the Princess IV ...
- [kuangbin带你飞]专题十二 基础DP1
A - Max Sum Plus Plus (HDU 1024) 题意:将n个数取m段且不相交,求m段数字和最大值: dp[i][j]:前i个数字分成j段的最大值. 边界dp[0][0] = 0; d ...
- kuangbin 专题十二: 基础DP1 Tickets
题目链接: 传送门 #include<cstdio> #include<cstring> #include<algorithm> using namespace s ...
- [kuangbin带你飞]专题十二 基础DP1 C - Monkey and Banana HDU - 1069
C - Monkey and Banana HDU - 1069 题目链接:https://vjudge.net/contest/68966#problem/C 题目: A group of rese ...
- [C#基础知识系列]专题十二:迭代器
引言: 在C# 1.0中我们经常使用foreach来遍历一个集合中的元素,然而一个类型要能够使用foreach关键字来对其进行遍历必须实现IEnumerable或IEnumerable<T> ...
- [C# 网络编程系列]专题十二:实现一个简单的FTP服务器
引言: 休息一个国庆节后好久没有更新文章了,主要是刚开始休息完心态还没有调整过来的, 现在差不多进入状态了, 所以继续和大家分享下网络编程的知识,在本专题中将和大家分享如何自己实现一个简单的FTP服务 ...
- 专题十二:实现一个简单的FTP服务器
引言: 在本专题中将和大家分享如何自己实现一个简单的FTP服务器.在我们平时的上网过程中,一般都是使用FTP的客户端来对商家提供的服务器进行访问(上传.下载文件),例如我们经常用到微软的SkyDriv ...
- kuangbin专题十六 KMP扩展KMP HDU3068 最长回文
给出一个只由小写英文字符a,b,c...y,z组成的字符串S,求S中最长回文串的长度. 回文就是正反读都是一样的字符串,如aba, abba等 Input输入有多组case,不超过120组,每组输入为 ...
- kuangbin专题十六 KMP扩展KMP HDU2594 Simpsons’ Hidden Talents
Homer: Marge, I just figured out a way to discover some of the talents we weren't aware we had. Marg ...
最新文章
- python获取url参数 类继承_python之类的继承
- python列表片段_Python列表片段索引操作,python
- 创建多线程_你真的了解多线程吗?
- github详细搜索
- Flask+uwsgi+Nginx环境搭建
- 分步表单_表单设计-掌握表单设计方法(表单体验篇)
- java jframe 设置背景图片_JFrame如何设置背景图片
- 基于matlab的gps信号仿真123,MATLABGPS信号仿真完整源代码.doc
- 使用Xshell连接Linux虚拟机(NAT)
- Windows部署Tomcat8启动服务
- 离散数学及其应用 第一章习题
- Intel ICH9 sata驱动
- 数据库课程设计——某商店进销存管理系统(附Java源码与课程设计报告)
- idea前端可视化_jsp可视化开发工具_netbeans jsp可视化_idea 可视化开发 jsp
- 山东农业大学计算机考研资料汇总
- bmp怎样转成jpg?
- 使用node实现简单的增删改查功能的小demo
- CryEngine技术讲解
- php二级分销kohana源码,php框架kohana(二)
- vs+cmake完美编译RTS游戏,类似魔兽争霸源码
热门文章
- atheros有线网卡LINUX驱动,新版Atheros AR81系列有线驱动
- python在线投票系统讲解_Python开发基础-项目实训-在线投票系统ppt课件
- 在VMware8.0下安装crux2.6
- 牛腩新闻发布--过程或函数 'news_selectByCaId' 需要参数 '@caid',但未提供该参数(一)
- 从键盘输入10个正负相间的整数,输出个位数是奇数、十位数是偶数的所有数。
- 《Hadoop权威指南》学习笔记(一)
- Flink跟着问题读源码 - SlidingEventTimeWindows接reduce结果数据倍增
- 公交换乘GIS地图应用解决方案
- IE6、IE7、IE8、IE9兼容性问题解决办法
- 五色石FCS周报 2019.5.13-2019.5.19