经典dp模型的变形(未完待续。。)
这几天做了几道dp题,发现有几道是很经典的dp模型的变形,总结一下:
数字三角形
这算是dp的最最最入门的题了吧,但是这题变形的话,还是需要好好想想才能看出来的。
题目:hdu 1176 免费馅饼
题意:
一定时间,天上会从某个位置Xi掉下一块馅饼,1< =xi< =10,这个人位于xi,只能接到xi-1,xi,xi+1,三处之一,问这人可以接到的最多馅饼?
分析:
这题想想还是挺有意思的QAQ,因为是按照时间落馅饼,所以dp的方向是显然的,就是按照时间,第二维当然就是位置了!所以用dp[i][j]表示在i时刻这个人在j位置可以得到的最大馅饼数,num[i][j]表示i时刻j位置落下的馅饼数,不难写出dp方程:
dp[i][x]=max(dp[i-1][x-1],dp[i-1][x],dp[i-1][x+1])+num[i][x]
想明白后,0时刻在5这个位置,然后顺着时间转移,转移到最后一块馅饼落下的时刻,那么这题不就做出来了吗?当然可以!注意下边界即可。
仔细想想这题,这不就是数字三角形吗?所以逆推很容易就可以搞定了!
const int N=1e5+2;
int n,x,T,t,f[N][11];
int main()
{
while(~scanf("%d",&n)&&n){
T=0;
memset(f,0,sizeof(f));
for(int i=0;i<n;i++){
scanf("%d%d",&x,&t);
f[t][x]++;
if(T<t)T=t;
}
for(int i=T-1;i>=0;i--){
for(int j=1;j<=9;j++)
f[i][j]+=max(f[i+1][j+1],max(f[i+1][j],f[i+1][j-1]));
f[i][0]+=max(f[i+1][0],f[i+1][1]);
f[i][10]+=max(f[i+1][10],f[i+1][9]);
}
cout<<f[0][5]<<endl;
}
return 0;
}
背包问题
背包问题无疑是变形最多的一类dp题,要想掌握好这类题目,关键还是要深刻理解01背包和完全背包!当然还有更多变形,像分组背包,有依赖的背包,泛化背包等,都是些不好掌握的东东,当然做题时如果能看出这是背包的题目,就离解决问题更近了一大
步!
(此处应有各种背包的分类,等多做几道题后再补QAQ)
完全背包
题目:hdu 1114 Piggy-Bank
题意:
存钱罐里有一些硬币,总共重量是F,空罐重量是E,给出n种钱币,每种钱币的有两个参数,v价值,w重量。问存钱罐中最少的价值是多少?
分析:
赤裸裸的完全背包的题目,注意初始化。
const int INF=0x3f3f3f3f;
const int N=1e4+9;
int n;
int E,F;
int f[N],p[N],w[N];
int main()
{
int T; scanf("%d",&T);
while(T--){
scanf("%d%d",&E,&F);
F-=E;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&p[i],&w[i]);
}
for(int i=1;i<=F;i++)f[i]=INF;
f[0]=0;
for(int i=1;i<=n;i++){
for(int v=w[i];v<=F;v++)
f[v]=min(f[v],f[v-w[i]]+p[i]);
}
if(f[F] == INF)
printf("This is impossible.\n");
else
printf("The minimum amount of money in the piggy-bank is %d.\n",f[F]);
}
return 0;
}
最长上升子序列 LIS
最长上升子序列变形是比较多的,可以先看我以前写的这篇:http://blog.csdn.net/hjt_fathomless/article/details/52176146
题目的话,有很多,比如矩形嵌套(按一边排序后,就可以LIS了),叠木块等!
题目:http://blog.csdn.net/hjt_fathomless/article/details/52176513
最长公共子序列 LCS
我记得以前在bestcoder上做过一道很好的变形题,不过忘了QAQ。等看到了再补吧!
不过重要的还是记住状态转移方程,知道怎么得到的!
简单的dp题
简单的dp题是什么东西?就是决策是显然的,一般递推就可以解决,这种题决策都不会太复杂(反正复杂的我也不会做QAQ),而且方向是明显的,所以是简单的。
比如走楼梯那种题,只有两个决策,一步or两步,方向是从0—n(楼底到楼上)。再比如上边所述的数字三角形,决策和方向都是很容易看出来的(所以才是入门题啊)。碰见这种题,那就偷着乐吧!
题目:hdu 1260 Tricks
题意:
售票员有两种选择,一次一票or两票,问花时最少?
分析:
这题是不是很简单,决策是显然的,一票or两票,方向从第一个人到最后一个人楼,这不是跟走楼梯那题一样吗?所以是简单dp!
const int N=1e5+2;
int a[N],b[N],f[N],t[5];
char c[5];
int main()
{
int n;
int T;scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=2;i<=n;i++)scanf("%d",&b[i]);
f[1]=a[1];
for(int i=2;i<=n;i++)
f[i]=min(f[i-1]+a[i],f[i-2]+b[i]);
t[1]=f[n]/3600; f[n]%=3600;
t[2]=f[n]/60; f[n]%=60;
t[3]=f[n];
t[1]+=8;
c[1]='a'; c[2]='m';
if(t[1]>12){
t[1]-=12;
c[1]='p';
}
printf("%02d:%02d:%02d %c%c\n",t[1],t[2],t[3],c[1],c[2]);
}
return 0;
}