DP练习~今天s酱不在家~
RT,今天s酱不在家~
于是w就可以为所欲为的刷水题啦~
因为w的DP学的实在是……,于是今天w去刷了几道DP题练手>_<
1.能量项链(luogu P1063)
这题面……考验语文能力的时刻到了x
一道区间DP
这个环结构看起来就很麻烦的样子……
那就拆了O(∩_∩)O~
每次读入一个a[i]的时候,顺手把a[n+i]也赋成这个值
这样处理完后就是一个长度为原环两倍的链
在这个链上能找到所有长度为n+1的可能排列
//长度n+1是因为最后一个珠子的尾标是第一个的头标,在链中即下一个的头标
接下来就DP吧
f[i][j]表示区间长度为i,区间左端点的头标记是j
有了这两个当然就能算出右端点的尾标记
然后枚举一波断点(区间DP的套路
因为每个珠子有两个标记,又只存了一个数
导致各种+1-1的小细节
qwq看了好久才明白
最后再统计一波答案
#include<iostream> #include<algorithm> using namespace std; const int N=202; int n,a[N],ans; int f[N][N]; int main() { scanf("%d",&n); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); a[i+n]=a[i]; } for(int i=2;i<=n+1;++i)//区间长度 { for(int l=1;l+i-1<=2*n;++l)//头标记 { int r=l+i-1;//尾标记 for(int k=l+1;k<=r-1;++k)//中间断点的标记 f[l][r]=max(f[l][r],f[l][k]+f[k][r]+a[l]*a[k]*a[r]); } } for(int i=1;i<=n;++i)//区间开始位置 ans=max(ans,f[i][i+n]); cout<<ans<<endl; return 0; }
2.垃圾陷阱(luogu P1156)
为什么是卡门不是Bessie(
刚一看题面各种mengbier,连DP存什么都想不出来
跑去看题解,类背包?++mengbier
看了好久才看懂……做法很神奇x
学了一种刷表的做法
(乱入的科普:刷表指用现在的状态去更新之后的状态,与之相反的是填表,指用之前的状态更新现在的状态)
DP数组?不存在的 这里的所谓DP数组其实是一个bool数组
里面存的,既不是高度,也不是能量
f[i][j]表示的是在高度为i,时间为j的情况下能否存活
如果能存活的话就向下转移
两个转移方向:堆起来(i+高度)或者吃掉(j+时间)
转移完发现高度超出井深了,因为经过了排序,所以得出的一定是最早的时间,输出就好了
一套循环下去这牛还没有出去,说明它已经在井里gg了
时间从后向前倒,看它活在这世上的最后一秒是什么时候
//传说中的刷表法qwq #include<cstdio> #include<iostream> #include<algorithm> using namespace std; int d,g,s=10; bool f[400][6000];//f[i][j]->高度为i的情况下能存活j的时间 struct rub{ int t,h,f; }r[105]; bool cmp(rub x,rub y) { return x.t<y.t; } int main() { cin>>d>>g; for(int i=1;i<=g;++i){ scanf("%d%d%d",&r[i].t,&r[i].f,&r[i].h); s+=r[i].f; //计算可能存活的最大时间 } sort(r+1,r+g+1,cmp); f[0][10]=1; for(int i=1;i<=g;++i)//垃圾 for(int j=d-1;j>=0;--j)//高度 for(int k=s;k>=r[i].t;--k)//时间 { if(!f[j][k])continue; if(j+r[i].h>=d) { printf("%d",r[i].t); system("pause"); return 0; } else { f[j+r[i].h][k]=1; f[j][k+r[i].f]=1; } } for(int i=s;i>=1;--i) if(f[0][i]) { printf("%d",i); system("pause"); return 0; } return 0; }
3.过河(luogu P1052)
一开始一看,这不是某只⑨的一道题吗?
然后一看数据范围,L<=10^9,1 <= M <= 100
(步长10桥长10^9,心疼这只青蛙一下x)
正常的DP方程很好写
f[j]=min(f[j],f[i]+stn[j])
然而10^9的数据,时间空间都会炸
但是因为长长的桥上只有很少的石子
所以在两个石子之间青蛙君会跳过一段很长的没有石子的距离
就可以把这段距离压缩一下
把距离>100(10*10)的两颗石子中间的距离压到100
……据说还可以压到90?并不理解qwq,反正有100就够用了
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int N=50000; int l,s,t,m,a[200],stn[N],f[N],tmp; int main() { scanf("%d%d%d%d",&l,&s,&t,&m); for(int i=1;i<=m;++i) scanf("%d",&a[i]); sort(a+1,a+m+1); if(s==t) { int ans=0; for(int i=1;i<=m;++i) if(!(a[i]%s))++ans; printf("%d",ans); return 0; } for(int i=1;i<=m;++i) { int x=a[i]-a[i-1]; if(x>100)tmp+=100; else tmp+=x; ++stn[tmp]; if(i==m)l=tmp+1; } int ans=200; for(int i=1;i<=l+15;++i)f[i]=200; f[0]=0; for(int i=0;i<=l+15;++i) { for(int j=i+s;j<=i+t;++j) f[j]=min(f[j],f[i]+stn[j]); } for(int i=l;i<=l+15;++i)ans=min(ans,f[i]); cout<<ans; return 0; }
呀s酱回来了>_<保存保存……