T4 跳房子 二分答案 单调队列
跳房子,也叫跳飞机,是一种世界性的儿童游戏,也是中国民间传统的体育游戏之一。
跳房子的游戏规则如下:
在地面上确定一个起点,然后在起点右侧画 n 个格子,这些格子都在同一条直线上。每个格子内有一个数字( 整数),表示到达这个格子能得到的分数。玩家第一次从起点开始向右跳, 跳到起点右侧的一个格子内。第二次再从当前位置继续向右跳,依此类推。规则规定:玩家每次都必须跳到当前位置右侧的一个格子内。玩家可以在任意时刻结束游戏,获得的分数为曾经到达过的格子中的数字之和。
现在小 R 研发了一款弹跳机器人来参加这个游戏。但是这个机器人有一个非常严重的缺陷,它每次向右弹跳的距离只能为固定的 d。小 R 希望改进他的机器人,如果他花 g 个金币改进他的机器人,那么他的机器人灵活性就能增加 g, 但是需要注意的是,每次弹跳的距离至少为 1。 具体而言, 当g < d时, 他的机器人每次可以选择向右弹跳的距离为 d-g, d-g+1,d-g+2, …, d+g-2, d+g-1, d+g; 否则( 当g ≥ d时),他的机器人每次可以选择向右弹跳的距离为 1, 2, 3, …, d+g-2, d+g-1, d+g。
现在小 R 希望获得至少 k 分,请问他至少要花多少金币来改造他的机器人。
我们定义 dp [ i ] 表示跳到 i 时,总数字之和的最大值。那么转移方程就比较容易了,dp [ i ] = max { dp [ j ] } + s [ i ](x [ i ] - r ≤ x [ j ] ≤ x [ i ] - l),这样做 O ( n ^ 2 ) 应该至少能拿到 50 分。下面,我来讲讲如何优化,可以过 500000 。P.S. x 应该是增加了代码的复杂度吧……
(请自行脑补一下 )当 i 不断递增向后走时,前面一定跟着一个区间,表示满足 x [ i ] - r ≤ x [ j ] ≤ x [ i ] - l 的 j。每次 i 加一后,会有头上部分 j 退出区间,也会有尾部部分 j 加入区间。此时,如果重新遍历区间求最大值,显然有些浪费了。我们维护好一个单调队列,包含区间内的部分 j,使得队头(左)的 dp 值较大,队尾(右)的 dp 值较小,每次取队头的为最大值。
如果存在一个 j,其后面有比自己 dp 值更大的 j',其实 j 是无用的。为什么呢?j 显然会比 j' 更早退出区间,当 j 在区间时,j' 显然会比 j 更优些。因此,单调队列中会自动滤掉无用的 j,保持 j 单调的同时 dp [ j ] 单调。接下来需要考虑的问题,就是如何维护了。
当 i 加一时,把区间右端的 j,即一直递增的游标 L,加入到队尾中。不过,应先将比 dp [ j ] 更小的(无用的)队尾弹出。之后,把队头已不在区间范围内的 j 弹出,剩下的队头就是所求的最大值了,更新入 dp [ i ]!在存在 i 使得 dp [ i ] ≥ k 时,就说明当前的 g 是符合条件的。
这个单调队列可以手动维护,但我使用了 STL 中的 deque,比较方便。我们再考虑一下复杂度,由于每个 j 只会进队一次、出队一次,时间复杂度应该是 O ( n ) 的。再乘上二分带的 log,显然可以过。
#include<cmath> #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<string> #include<map> const int maxn=500005; using namespace std; //int n,d,k; //int len[maxn],value[maxn],dp[maxn],que[maxn*2]; //(因为单调)二分+动态规划+(用单调队列优化) //我觉得单调队列最难理解 //当每次i加1后,会有上面部分j退出区间,也会有尾部的j加入区间,此时如果重新遍历区间求最大值就会浪费---所以维护一个单调队列 //包含区间内的部分j,使得对头的dp最大,队尾的dp最小,每次取队头的位最大值 //单调队列会自动过滤无用的j,保持dp[j]单调 /* int maxcost(int a){ memset(dp,-127,sizeof(dp)); int head=1,tail=0; int mi=max(1,d-a); int mx=d+a; int rt=0,fir=-1; que[++tail]=0,dp[0]=0; for(int i=1;i<=n;i++){ if(len[i]<mi) continue; if(len[i]>=mi&&fir==-1) fir=i; if(len[i]-len[i-1]>mx) break; //过不去 while(len[i]-len[fir]>=mi&&fir<i){ while(head<=tail&&dp[fir]>dp[que[tail]]) tail--; //把无用的队尾丢掉 if(dp[fir]!=-0x7f7f7f7f) que[++tail]=fir; //把对头的弄到队尾来? fir++; } while(head<=tail&&len[que[head]]+mx<len[i]) head++; //把无用的队头丢掉 if(head>tail) dp[i]=-0x7f7f7f7f; else dp[i]=dp[que[head]]+value[i]; if(dp[i]>rt) rt=dp[i]; } return rt; } */ //上面是单调队列的写法,还可以用deque双端队列 //下面直接用动态规划也可以 long long f[maxn],a[maxn][2],n,d,k,lpos,rpos; bool check(int g){ lpos=d-g;rpos=d+g; if(lpos<=0) lpos=1; memset(f,-127,sizeof(f));//先把dp数组初始化为一个很小的值 f[0]=0 ; //起点 for(int i=1;i<=n;i++){ for(int j=i-1;j>=0;j--){ //用前面的去优化后面的 if(a[i][0]-a[j][0]<lpos) continue; //太靠左 if(a[i][0]-a[j][0]>rpos) break; //太靠右 f[i]=max(f[j]+a[i][1],f[i]); if(f[i]>=k) return 1; } } return 0; } int main(){ cin>>n>>d>>k; /* for(int i=1;i<=n;i++){ cin>>len[i]>>value[i]; } int left=0,right=len[n]; while(left!=right){ int mid=(left+right)>>1; if(maxcost(mid)>=k) right=mid; else left=mid+1; } if(maxcost(left)<k) cout<<"-1"<<endl; else cout<<left<<endl; */ for(int i=1;i<=n;i++){ cin>>a[i][0]>>a[i][1]; } int ans=-1,l=0,r=20001,m=(l+r)/2; while(l<=r){ if(check(m)){ ans=m; r=m-1; } else l=m+1; m=(l+r)/2; } cout<<ans<<endl; return 0; }
T3
棋盘
这道题是用DFS写的,因为要记录上一个格子的情况(有没有变色)所以参数需要三个,而且需要回溯,从而改变那个可不可以的参数
#include<iostream> #include<cstring> #include<cmath> #include<algorithm> #include<stack> #include<cstdio> #include<queue> #include<map> #include<vector> #include<set> using namespace std; const int maxn=1010; const int INF=0x3fffffff; int mapp[110][110]; int cost[110][110]; //这个存放的是到这里的最小花费 int m,n,x,y,sig; int tot=0; int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; void dfs(int x,int y,int flag){ //flag是能不能走的标志 if(x==m&&y==m) return; for(int i=0;i<4;i++){ int xx=x+dis[i][0],yy=y+dis[i][1]; if(xx>=1&&xx<=m&&yy>=1&&yy<=m){ int ans=-1; //表示花费 if(mapp[xx][yy]==mapp[x][y]&&mapp[xx][yy]!=0) ans=0; else{ if(mapp[xx][yy]!=0) ans=1; //如果颜色不同 if(!mapp[xx][yy]&&flag) ans=2; //如果没有颜色,而且可以变色 } if(ans!=-1&&(cost[x][y]+ans<cost[xx][yy]||!cost[xx][yy])){ cost[xx][yy]=cost[x][y]+ans; //如果现在到那里去的代价小于他本来的代价,或者还没有去过 if(ans==2){ mapp[xx][yy]=mapp[x][y]; dfs(xx,yy,0); mapp[xx][yy]=0; //要回溯 } else dfs(xx,yy,1); } } } } int main(){ cin>>m>>n; memset(mapp,0,sizeof(mapp)); for(int i=0;i<n;i++){ cin>>x>>y>>sig; mapp[x][y]=sig+1; } cost[1][1]=1; dfs(1,1,1); cout<<cost[m][m]-1<<endl; return 0; } /* int flag=0,flag1=0; if(x==m&&y==m) return tot; for(int i=0;i<4;i++){ int xx=x+dis[i][0],yy=y+dis[i][1]; if(xx>=1&&xx<=m&&yy>=1&&yy<=m&&mapp[xx][yy]==tag){ cout<<xx<<" "<<yy<<" "<<tag<<" "<<tot<<endl; flag=1; dfs(xx,yy,tag,tot); } } if(flag==0){ for(int i=0;i<4;i++){ int xx=x+dis[i][0],yy=y+dis[i][1]; if(tag==1) tag=0; else tag=1; if(xx>=1&&xx<=m&&yy>=1&&yy<=m&&mapp[xx][yy]==tag){ cout<<xx<<" "<<yy<<" "<<tag<<" "<<tot<<endl; flag1=1; dfs(xx,yy,tag,tot+1); } } if(flag1==0){ for(int i=0;i<4;i++){ int xx=x+dis[i][0],yy=y+dis[i][1]; for(int j=0;j<4;j++){ int xxx=xx+dis[j][0];int yyy=yy+dis[j][1]; if(xxx>=1&&xxx<=m&&yyy>=1&&yyy<=m&&mapp[xxx][yyy]!=-1) { cout<<xx<<" "<<yy<<" "<<tag<<" "<<tot<<endl; dfs(xx,yy,mapp[xxx][yyy],tot+2); } } } } } return -1; */