2012百度之星冬季赛第二场第二题 消去游戏I
2012-12-24 18:27
176 查看
题目:
Alice和Bob又开始发明新游戏了,这回的名字叫消去游戏。
消去游戏的道具是一堆排成一行的积木,每个积木上面都有一个数字Ai。同时游戏也需要M个额外的数字Di,作为消去的判断条件。当连续的K个积木数字相邻的差值都相同并且和某个Di相等时,即构成一个长度为K等差为Di的等差数列时,他们可以拿去这一段积木,然后不改变剩余积木的顺序,将他们合并。显然,每次至少要消去2个积木。
现在他们希望知道,在一个给定局面下,能最多消去的积木数目。
Input
输入第一行为T,表示有T组测试数据。
每组数据以两个整数N,M开始,具体意义与描述相同。接着的一行包括N个整数,表示排成一行的积木数字Ai。接下来的一行是M个整数,即给定的消去差值Di。
1. 1 <= T <= 100
2. 1 <= N, M <= 300
3. -1 000 000 000 <= Ai, Di <= 1 000 000 000
Output
对每组数据,先输出为第几组数据,然后输出最多能消去的积木数目。
Sample Input
3
3 1
1 2 3
1
3 2
1 2 4
1 2
4 2
1 3 4 3
1 2
Sample Output
Case 1: 3
Case 2: 2
Case 3: 4
Hint
第三组数据中,可以先消去第二个和第三个积木,然后消去剩下变为相邻的两个积木。注意不能直接消去第三个和第四个积木,因为它们的差值是-1,而不是1。
个人题解,结果公布前,暂时不保证能AC:已AC
我的解法是DP的,为了便于思考,程序里面用了两次DP,两个其实可以合并的,因为状态其实都是区间的消去情况。
首先,一次可以消去多个,这个条件必须转化,不然很不利于状态转移,很快就可以发现,假如每次仅允许消去2个是不够的,但是假如每次允许消去2个或者3个,其实就够了,不管多长的区间总可以拆分为每次消去两个和三个的组合。
然后第一个bool dp
,dp[i][j]表示区间[i, j]可不可以消去
用map储存允许的差值,用mp[d]==true来表示差值允许
a[i]表示原序列第i个数字
显然
一、i>=j时,dp[i][j]=false
感谢 ACdream的[scu]09p17(copy卡卡西)童鞋指正
(哎,程序果然还有bug,过了只是运气,我还是太连清了TAT)
1. i == j时,此时区间为一个数,无法消去,dp[i][j]=false
2. i > j时,此时区间为空区间,dp[i][j]=true
二、i+1==j时,mp[a[j] - a[i]]
三、i+2==j时,a[j] - a[j-1] == a[j-1] - a[i] && mp[a[j] - a[j-1]]
四、i+2<j
这时,根据上面得出的,每次删两个或三个这个条件,假如dp[i][j]要等于true,分为两种情况
1.区间由两个可删除区间组成
也就是假如存在i<k<j使得 dp[i][k] && dp[k+1][j]
2.区间是删除了一些子区间后,剩下来的两个或三个数字,正好可以消去
①剩下两个可以消去,则这两个必然是头尾,否则在情况1里已经讨论
即dp[i+1][j-1] && mp[a[j] - a[i]]
②剩下三个可以消去,同①理,其中肯定有头尾
即存在i<k<j使得 a[j] - a[k] == a[k] - a[i] && dp[i+1][k-1] && dp[k+1][j-1] && mp[a[j] - a[k]]
这样就讨论完了,由于map的速度较慢,而且有很多的状态为false,所以写最后,优化下常数
状态转移较为复杂,就写成记忆化搜索了,比较简洁。
得到上面的dp[i][j]后
我们用ans[i]表示区间[0, i]最多能消去多少个,则
ans[0]=0;
ans[i] = Max(
ans[i-1], //a[i]没有被消去
Max(ans[j-1] + dp[j][i]?j-i+1:0)(0<j<i) //a[i]被消去,则肯定有区间[j, i]被消去,消去数为j-i+1,加上之前的区间[0, j-1]的最大消去数
)
最终我们得到的ans[n-1]即为区间[0, n-1]的最大消去数,问题得以解决。第一个dp状态数N^2,转移费用由于枚举k最大为N,时间复杂度为N^3, 不过在实际中,由于i>=j的状态基本没用到,加上一旦得出dp[i][j]=true就return的写法,实际应该不容易达到这个上限。
ans这部分DP状态数为N,转移费用为N
综合时间复杂度为O(n^3),N=300,还是有些紧张的。结果出来之前,个人觉得觉得有一定可能TLE。百度当天给出两个√。
如果这题直接用dp[i][j]表示区间[i, j]的最大消去数应该也是可以的,最终答案为dp[0, n-1]
综合上面两个DP状态转移方程应该也是可以得到比较简单的状态转移方程,不过木有地方测试啊。。。。
[b]是不是还有可能用四边形优化,求DP大神讨论!!!!![/b]
以下代码:(百度之星比赛有代码长度的分数,变量为了简介并不与上面题解中一一对应,望谅解)
bug修改版
Alice和Bob又开始发明新游戏了,这回的名字叫消去游戏。
消去游戏的道具是一堆排成一行的积木,每个积木上面都有一个数字Ai。同时游戏也需要M个额外的数字Di,作为消去的判断条件。当连续的K个积木数字相邻的差值都相同并且和某个Di相等时,即构成一个长度为K等差为Di的等差数列时,他们可以拿去这一段积木,然后不改变剩余积木的顺序,将他们合并。显然,每次至少要消去2个积木。
现在他们希望知道,在一个给定局面下,能最多消去的积木数目。
Input
输入第一行为T,表示有T组测试数据。
每组数据以两个整数N,M开始,具体意义与描述相同。接着的一行包括N个整数,表示排成一行的积木数字Ai。接下来的一行是M个整数,即给定的消去差值Di。
1. 1 <= T <= 100
2. 1 <= N, M <= 300
3. -1 000 000 000 <= Ai, Di <= 1 000 000 000
Output
对每组数据,先输出为第几组数据,然后输出最多能消去的积木数目。
Sample Input
3
3 1
1 2 3
1
3 2
1 2 4
1 2
4 2
1 3 4 3
1 2
Sample Output
Case 1: 3
Case 2: 2
Case 3: 4
Hint
第三组数据中,可以先消去第二个和第三个积木,然后消去剩下变为相邻的两个积木。注意不能直接消去第三个和第四个积木,因为它们的差值是-1,而不是1。
个人题解,结果公布前,暂时不保证能AC:已AC
我的解法是DP的,为了便于思考,程序里面用了两次DP,两个其实可以合并的,因为状态其实都是区间的消去情况。
首先,一次可以消去多个,这个条件必须转化,不然很不利于状态转移,很快就可以发现,假如每次仅允许消去2个是不够的,但是假如每次允许消去2个或者3个,其实就够了,不管多长的区间总可以拆分为每次消去两个和三个的组合。
然后第一个bool dp
,dp[i][j]表示区间[i, j]可不可以消去
用map储存允许的差值,用mp[d]==true来表示差值允许
a[i]表示原序列第i个数字
显然
一、i>=j时,dp[i][j]=false
感谢 ACdream的[scu]09p17(copy卡卡西)童鞋指正
(哎,程序果然还有bug,过了只是运气,我还是太连清了TAT)
1. i == j时,此时区间为一个数,无法消去,dp[i][j]=false
2. i > j时,此时区间为空区间,dp[i][j]=true
二、i+1==j时,mp[a[j] - a[i]]
三、i+2==j时,a[j] - a[j-1] == a[j-1] - a[i] && mp[a[j] - a[j-1]]
四、i+2<j
这时,根据上面得出的,每次删两个或三个这个条件,假如dp[i][j]要等于true,分为两种情况
1.区间由两个可删除区间组成
也就是假如存在i<k<j使得 dp[i][k] && dp[k+1][j]
2.区间是删除了一些子区间后,剩下来的两个或三个数字,正好可以消去
①剩下两个可以消去,则这两个必然是头尾,否则在情况1里已经讨论
即dp[i+1][j-1] && mp[a[j] - a[i]]
②剩下三个可以消去,同①理,其中肯定有头尾
即存在i<k<j使得 a[j] - a[k] == a[k] - a[i] && dp[i+1][k-1] && dp[k+1][j-1] && mp[a[j] - a[k]]
这样就讨论完了,由于map的速度较慢,而且有很多的状态为false,所以写最后,优化下常数
状态转移较为复杂,就写成记忆化搜索了,比较简洁。
得到上面的dp[i][j]后
我们用ans[i]表示区间[0, i]最多能消去多少个,则
ans[0]=0;
ans[i] = Max(
ans[i-1], //a[i]没有被消去
Max(ans[j-1] + dp[j][i]?j-i+1:0)(0<j<i) //a[i]被消去,则肯定有区间[j, i]被消去,消去数为j-i+1,加上之前的区间[0, j-1]的最大消去数
)
最终我们得到的ans[n-1]即为区间[0, n-1]的最大消去数,问题得以解决。第一个dp状态数N^2,转移费用由于枚举k最大为N,时间复杂度为N^3, 不过在实际中,由于i>=j的状态基本没用到,加上一旦得出dp[i][j]=true就return的写法,实际应该不容易达到这个上限。
ans这部分DP状态数为N,转移费用为N
综合时间复杂度为O(n^3),N=300,还是有些紧张的。结果出来之前,个人觉得觉得有一定可能TLE。百度当天给出两个√。
如果这题直接用dp[i][j]表示区间[i, j]的最大消去数应该也是可以的,最终答案为dp[0, n-1]
综合上面两个DP状态转移方程应该也是可以得到比较简单的状态转移方程,不过木有地方测试啊。。。。
[b]是不是还有可能用四边形优化,求DP大神讨论!!!!![/b]
以下代码:(百度之星比赛有代码长度的分数,变量为了简介并不与上面题解中一一对应,望谅解)
bug修改版
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> #include<map> using namespace std; const int N=310; map<int,bool>mp; int T,C,n,m,a ,s ,x; char q ; int dfs(int x,int y){ char &ans=q[x][y]; if(ans>=0) return ans; if(y-x<0) return ans=1; if(y==x) return ans=0; if(y-x==1) return ans=mp[a[y]-a[x]]; if(y-x==2) return ans=(a[y]-a[y-1]==a[y-1]-a[x] && mp[a[y-1]-a[x]]); ans=0; for(int i=x+1;i<y && !ans;i++){ if(dfs(x,i) && dfs(i+1,y))ans=1; if(a[i]-a[x] == a[y]-a[i] && dfs(x+1,i-1) && dfs(i+1,y-1) && mp[a[i]-a[x]]) ans=1; } if(ans) return ans; if(dfs(x+1,y-1) && mp[a[y]-a[x]]) ans=1; return ans; } int main() { scanf("%d",&T); while(T--){ printf("Case %d: ",++C); scanf("%d%d",&n,&m); mp.clear(); for(int i=0;i<n;i++)scanf("%d",a+i); for(int i=0;i<m;i++){ scanf("%d",&x); mp[x]=true; } if(n==0){ puts("0"); continue; } memset(q,-1,sizeof q); for(int i=1;i<n;i++){ if(dfs(0,i)){ s[i]=i+1; continue; } s[i]=s[i-1]; for(int j=1;j<i;j++)if(dfs(j,i) && s[j-1]+i-j+1>s[i]) s[i]=s[j-1]+i-j+1; } printf("%d\n",s[n-1]); } return 0; }
相关文章推荐
- 2012百度之星冬季赛第四场第二题 度熊的复仇
- 2012百度之星第二场初始题-1
- 2012百度之星初赛第二场A题解答
- 百度之星2012初赛第二场C, 网页聚类, 二分
- 百度之星2010 复赛第二场 第二题 题目 解题报告
- 2012百度之星初赛第二场ProblemC
- 百度之星冬季邀请赛第二题
- 【noip2012提高组】国王游戏
- Cocos2d-x简单游戏<捕鱼达人>代码实现|第二部分:子弹、渔网、大炮类
- BZOJ 2756 SCOI 2012 奇怪的游戏 二分+最大流
- bzoj2756 [SCOI2012]奇怪的游戏
- 洛谷 [NOIP2012 D1T2] P1080 国王游戏
- cocos2d-x加box2d制作弹弓类游戏 第二部分
- 直线交点的凸包(百度之星2009初赛第二场第三题)
- CSP 2017.12 第二题 游戏 java代码
- 洛谷 1080 [NOIP2012] 国王游戏 贪心+高精
- 19. 如何使用GameCenter制作一个简单的多人游戏教程:第二部分
- 【2012百度之星/资格赛】C: 易手机的套餐
- 【2012百度之星/资格赛】G:聊天就是Repeat
- 【NOIP2012】国王游戏