Chapter 32: 动态规划小测总结 (USACO的???
前言+总结
四道题两个小时,都是2~3星的DP。最终300分,诶DP弱到不行的我也算是这样了吧orz。
花了快一个小时搞第一题(发现自己好搞笑啊,一直觉得自己的方程很简单会不会漏了什么一直在看= =);然后跳了第二题去打第三题,用了大概十五分钟,感觉这个细节蛮多的,打得我好虚啊;最后一题也大概打了十五分钟吧,挺水的,,一开始还想弄个一维的算了,发现不行= =,就直接改成滚动了√然后重新搞了几下这三题,静态查错了一下,好像没什么不对的,就剩十分钟了,ORZ,没时间搞第二题了QwQ【还想拼拼手速十分钟第二题来着完了发现方法不对啊
果然都要先思考啊。
怎么说,看完题有个大概思路还是很重要的,有时候就不要想那么多那些有的没的= =简直浪费时间。
插曲:
刚打完第一题的我,发现hyc一脸无奈(???)的看着我,我就冲她摇摇头表示我也不会做;蒟蒻打完第三题的时候,发现她又看着我,我表示疑惑,于是她说:我刚刚打了1、4题的对拍,发现我第1题sabi了。。。我:???你打完了?!!! hyc:是啊还打了两个对拍。我:ORZORZORZ...too naive的我
题目+题解
第一题
因为只有两个苹果树,且知道了起始位置,那么当确定了移动了多少次的时候就能确定此时处在那一棵树下。
于是设f[i][j],表示i时刻时已经移动了j次。
转移方程为:f[i][j]=max(f[i-1][j],f[i-1][j-1])+(该状态下有没有苹果接)
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> typedef long long LL; using namespace std; #define N 1100 #define M 50 int f[N][M],a[N]; int mymin(int x,int y){return (x<y)?x:y;} int mymax(int x,int y){return (x>y)?x:y;} int main() { //freopen("bcatch.in","r",stdin); //freopen("bcatch.out","w",stdout); int n,k,i,j,ans=0; memset(f,0,sizeof(f)); scanf("%d%d",&n,&k); for (i=1;i<=n;i++) scanf("%d",&a[i]); for (i=1;i<=n;i++) { f[i][0]=f[i-1][0]+(a[i]==1); for (j=1;j<=k;j++) { if (j>i) break; f[i][j]=mymax(f[i-1][j],f[i-1][j-1]); if (a[i]%2==(j+1)%2) f[i][j]+=1;//判断此状态有没有苹果接 ans=mymax(ans,f[i][j]); } }printf("%d\n",mymax(ans,f[n][0]));//本来没有把一直不动的更新到ans里= =数据弱啊 return 0; }
============================================
第二题
我跳了。。因为两种东西感觉好麻烦= =
好吧说解法。当你发现能力值只有100的时候,就很容易想到把 什么时候都能去滑 的那个给预处理了。
设bs[i],表示当能力值为i时,能去滑的滑雪中一次所耗费的最小时间。
f[i],就表示一定上第i门课时能滑的最大次数。
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define T 100100 struct node { int m,l,a; }cl[T];int bs[110]; int f[T]; bool cmp(node x,node y) { if (x.m!=y.m) return x.m<y.m; if (x.a!=y.a) return x.a-y.a; return x.l<y.l; } int mymin(int x,int y){return (x<y)?x:y;} int mymax(int x,int y){return (x>y)?x:y;} int main() { //freopen("ski.in","r",stdin); //freopen("ski.out","w",stdout); int t,s,n,i,j,x,y,ans=0; scanf("%d%d%d",&t,&s,&n); for (i=1;i<=s;i++) scanf("%d%d%d",&cl[i].m,&cl[i].l,&cl[i].a); memset(bs,63,sizeof(bs)); memset(f,0,sizeof(f)); for (i=1;i<=n;i++) { scanf("%d%d",&x,&y); bs[x]=mymin(bs[x],y); }//预处理bs[] for (i=2;i<=100;i++) bs[i]=mymin(bs[i-1],bs[i]); sort(cl+1,cl+1+s,cmp);//对课程排下序 cl[0].m=cl[0].l;cl[0].a=1; for (i=1;i<=s;i++) if (cl[i].m+cl[i].l<=t) //看看上完这门课程是否还在限制内 { for (j=0;j<i;j++)//枚举上一次上的课是那个 { int sc=(cl[i].m-(cl[j].m+cl[j].l))/bs[cl[j].a]; //sc就是上次上完课后到这次上课前去滑雪能滑的(最大)次数 f[i]=mymax(f[i],f[j]+sc); } ans=mymax(ans,f[i]); ans=mymax(ans,f[i]+(t-cl[i].m-cl[i].l)/bs[cl[i].a]); } ans=mymax(ans,t/bs[1]);//这是不上课的 printf("%d\n",ans); return 0; }
===========================================
第三题
这道题,额贪心水了过去。因为过程中发现自己漏了一些细节,打得好虚!还好都有考虑到。
先按位置排序,去重(某点多个限制就去最小)。[然而似乎数据并不需要我做这些- -
我是直接贪在两个限制间我能到的最大速度嘛,所以首先要保证,当我刚好处于前面位置的限制速度时,我一定能保证后面不会超速。所以就从后往前扫一遍,用后面的限速来限制前面的。以此来保证肯定合法。
处理完这些,就直接扫一遍啊,两个限制点间的速度肯定呈 先递增后递减 或 单调递增/减,而这些取决于这两个点限制速度的大小关系。【不造怎么说。。看代码吧orzorzorz
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long LL; #define maxn 101000 LL mymin(LL x,LL y){return (x<y)?x:y;} LL mymax(LL x,LL y){return (x>y)?x:y;} struct node {LL w,v;}a[maxn]; bool cmp(node x,node y) { if (x.w!=y.w) return x.w<y.w; return x.v<y.v; } int main() { //freopen("bobsled.in","r",stdin); //freopen("bobsled.out","w",stdout); LL l,n,i,ln,ans,v,w; scanf("%I64d%I64d",&l,&n); for (i=1;i<=n;i++) scanf("%I64d%I64d",&a[i].w,&a[i].v); sort(a+1,a+1+n,cmp);ln=0; for (i=1;i<=n;i++) if (a[i].w!=a[i-1].w) a[++ln]=a[i];//去重 n=ln;ans=0;v=1;w=0; for (i=n-1;i>=1;i--)//保证合法性 a[i].v=mymin(a[i].v,a[i+1].w-a[i].w+a[i+1].v); for (i=1;i<=n;i++) { if (a[i].v>=v) { if (a[i].v-v<=a[i].w-w)//速度曲线呈先递增后递减 { ans=mymax(ans,a[i].v+((a[i].w-w)-(a[i].v-v))/2); v=a[i].v; }else {ans=mymax(ans,v+(a[i].w-w));v+=(a[i].w-w);} //单调递增 注意,此时速度不一定能到达限制速度所以不能直接v=a[i].v }else { ans=mymax(ans,v+((a[i].w-w)-(v-a[i].v))/2);//单调递减 v=a[i].v; }w=a[i].w; }ans=mymax(ans,v+(l-w));//不要忘了能一直加速至终点! printf("%I64d\n",ans); return 0; }
=============================================
第四题
这个很好想啊,f[i]就表示余数为i的有多少个。
数据范围明显让你O(nm)啊,就这样搞搞就好了。
不要作死想一维过了,还是好好打二维吧
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #include<iostream> typedef long long LL; using namespace std; #define maxn 2100 #define mod 100000000 LL f[2][maxn];int a[maxn]; int main() { //freopen("fristeam.in","r",stdin); //freopen("fristeam.out","w",stdout); int n,m,i,j,t; scanf("%d%d",&n,&m); for (i=1;i<=n;i++) { scanf("%d",&a[i]); a[i]=a[i]%m; }t=1; memset(f,0,sizeof(f)); for (i=1;i<=n;i++) { for (j=m-1;j>=0;j--) { f[t][(j+a[i])%m]+=f[1-t][j]; f[t][(j+a[i])%m]=f[t][(j+a[i])%m]%mod; }f[t][a[i]]++;f[t][a[i]]%=mod; for (j=0;j<=m-1;j++) { f[t][j]=(f[t][j]+f[1-t][j])%mod; f[1-t][j]=0; }t=1-t; } printf("%I64d\n",f[1-t][0]); return 0; }