20190817DP练习
ennn,今天是0x125E591的生日 大家+1s.-xcc
T1:LG奇怪的电梯.
S1:对每一个点建合法的边,跑最短路.(ps:因为不是DP,不上码了.)
T2:LG尼克的任务.
S2:遗憾考试卡在这个题上,最后连暴力都没打.看到给定一段段区间关联交并问题,自然地联想到了昨天做LG上的饥饿的奶牛 ,于是设了f[ i ][ 0/1]表示为第 i 段选或不选并保证前 i 段都会合法覆盖的情况下最少工作时间,f[ i ][ 0 ]则枚举i之前的区间k能覆盖i区间的进行f[ i ][ 0 ]=max{f[k][1]}的转移,f[ i ][ 1 ]则变得不好处理,要去找不覆盖i区间的区间k,并找到最小代价v将Σk覆盖,f[ i ][1]=max(f[ i ][ 1],v+time[i]),最后发现v怎么也找不出来,并发现后面的选择还会影响前面的状态,这样推dp显然是不行的.看了题解,发现果然要倒推,因为后面的状态影响前面的状态.记录此时不是任务开头,就f[ i ]=f[ i+1]+1,有任务就执行,f[ i ]=Σmax(f[ i ],f[ i+len]),建议调试样例,自然会懂.
总结:①平静下来,不会正解打暴力.②MS:code once,think twice.
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; #define e exit(0) #define R register int n,k,id=1,sum[10010],f[10010]; struct bian{ int L,v; }len[10010]; bool cmp(bian a,bian b){return a.L>b.L;} int main() { freopen("LIGNJA.in","r",stdin); freopen("LIGNJA.out","w",stdout); scanf("%d%d",&n,&k); for(R int i=1;i<=k;++i){ scanf("%d%d",&len[i].L,&len[i].v); ++sum[len[i].L]; } sort(len+1,len+1+k,cmp); for(R int i=n;i>=1;--i){ if(!sum[i]) f[i]=f[i+1]+1; else{ for(R int j=1;j<=sum[i];++j){ f[i]=max(f[i],f[i+len[id].v]); ++id; } } } printf("%d",f[1]); }
T5:LGP1233
S5:不知道为什么考试脑抽,对于双要求的最长不下降子序列,自己就将两个要求一起判断求???其实要联想到LG友好城市.将一个要求sort确定下来,另一个在此基础上求.
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; #define e exit(0) #define R register int n,q[5010],lenth; struct bian{ int L,W; }len[5010]; bool cmp(bian a,bian b){ if(a.L==b.L) return a.W<=b.W; else return a.L<b.L; } int find(int x) { int l=1,r=lenth; while(l<r){ int mid=(l+r)>>1; if(q[mid]<=x) r=mid; else l=mid+1; } return l; } int main() { freopen("STICK.in","r",stdin); freopen("STICK.out","w",stdout); scanf("%d",&n); for(R int i=1;i<=n;++i) scanf("%d%d",&len[i].L,&len[i].W); sort(len+1,len+1+n,cmp); q[++lenth]=len[1].W; for(R int i=2;i<=n;++i) { if(len[i].W<q[lenth]) q[++lenth]=len[i].W; else if(len[i].W>=q[lenth]){ int id=find(len[i].W); q[id]=len[i].W; } } printf("%d",lenth); return 0; }
PS:①:当一个元素一个标志相同时,另一个标志小sort时放前,这样单调栈求最长下降子序列时长度会往小的方面发展.
②:二分查找找维护单调下降的序列中第一个比它小的是这样写,其他要求跟题目意思来,注意等号给左边给右边关系"比它小比它大问题".
T4:LGP1220
S4:太菜了,看题解才懂.设f[ L ][ R ][ 0/1 ]表示为走完区间[ L , R ]的耗电量,并且结束时人在L / R点.在转移方程中我们要体现一直走或折返走这个过程.思路转移在题解中写的很清楚了.但有几个细节蒟蒻认为比较重要,想了一下才明白.
① f[ L ][ R ][ 0 ]会由f[ L+1 ][ R ][ 1 ]转移过来,顺推,L由L+1转移,看似违背了后效性,其实不然,我们会发现len从小到大枚举,f[ L+1 ][ R ]会提前更新.
② 对于任意的f[ L ][ R ][ 0 ]不一定都有意义(可对样例的各个区间输出一下).因为开始位置决定了有些区间无法走的,这就证明了为什么初始化是有意义的.
#include<iostream> #include<cstring> #include<cstdio> using namespace std; #define re register long long n,c,pos[1010],sum[1010],w[1010],f[1010][1010][2]; int main() { freopen("power.in","r",stdin); freopen("power.out","w",stdout); scanf("%lld%lld",&n,&c); for(re int i=1;i<=n;++i){ scanf("%lld%lld",&pos[i],&w[i]); sum[i]=sum[i-1]+w[i]; } memset(f,0x7f,sizeof(f)); f[c][c][0]=f[c][c][1]=0; for(re int len=2;len<=n;++len) for(re int L=1;L+len-1<=n;++L){ int R=L+len-1; f[L][R][0]=min(f[L+1][R][0]+(pos[L+1]-pos[L])*(sum[L]+sum[n]-sum[R]),f[L+1][R][1]+(pos[R]-pos[L])*(sum[L]+sum[n]-sum[R])); f[L][R][1]=min(f[L][R-1][1]+(pos[R]-pos[R-1])*(sum[L-1]+sum[n]-sum[R-1]),f[L][R-1][0]+(pos[R]-pos[L])*(sum[L-1]+sum[n]-sum[R-1])); } printf("%lld",min(f[1][n][0],f[1][n][1])); return 0; }
T3:对于一个多边形来说,在该多边形内任取两点,如果这两点连成的线段落在多边形内,则称这样的多边形为凸多边形。平面上有 N 个坐标值为自然数的圆点。顶点数最多凸多边形是指由给定的圆点中的一部分组成的凸多边形,它包含最大可能的顶点数。原点,即坐标内中心(0,0)必须是顶点数最多凸多边形的一个顶点。编写程序求出这样的凸多边形的最大顶点数。注意一个多边形的连续的边不能是平行的.(好像OJ没这个题)
S3:依旧太菜了,看题解才懂.先对坐标进行极角排序(为了保证DP的无后效性).设 f[ i ][ j ]为 i为倒数第二个点,j为倒数第一个点构成的凸多边形有最多点,对当前f[ i ][ j ],我们枚举点k,如果k , i , j能构成凸多边形,我们进行f[ i ][ j ]=max{f[ k ][ i ]+1},即在f[ i ][ k ]构成的凸多边形加入 j点,这时f[ k ][ i ]-->f[ i][ j ].
细节:①我们将'0' , ' n+1 '其实视为一个点(原点).所以 j 要枚举到 n+1,其实为了最后ans+1(加上原点),k从0开始枚举,是为了f[ 0 ][ i ]=1的初始化起作用.
②三点是否在同一凸多形,本质是用叉乘判断.对于( a,b),(c,d),(e,f),形成两个向量①( a - c, b- d),②(c - e,d-f).①x②>0即在同一凸多边形.化简为a(d-f)+c(f-b)+e(b-d)>0
#include<iostream> #include<algorithm> #include<cstring> #include<cstdio> using namespace std; #define R register int n,ans,f[110][110]; struct dian{ int x,y; bool operator<(const dian &a) const{ return 1.0*y/x<1.0*a.y/a.x; } }dot[110]; bool check(int x,int y,int z) { int a=dot[x].x,b=dot[x].y; int c=dot[y].x,d=dot[y].y; int e=dot[z].x,f=dot[z].y; return (a*(d-f)+c*(f-b)+e*(b-d) > 0); } int main() { freopen("POLYGON.in","r",stdin); freopen("POLYGON.out","w",stdout); scanf("%d",&n); for(R int i=1;i<=n;++i) scanf("%d%d",&dot[i].x,&dot[i].y); sort(dot+1,dot+1+n); for(R int i=1;i<=n;++i) f[0][i]=1; for(R int i=1;i<=n;++i) for(R int j=1;j<=n+1;++j) for(R int k=0;k<i;++k) if(check(i,j,k)) f[i][j]=max(f[i][j],f[k][i]+1); for(R int i=1;i<=n;++i) ans=max(ans,f[i][n+1]); printf("%d",ans); return 0; }