2012百度之星冬季赛第二场第二题 消去游戏I
題目:
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[N][N],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狀態轉移方程應該也是可以得到比較簡單的狀態轉移方程,不過木有地方測試啊。。。。
?
?
是不是還有可能用四邊形優化,求DP大神討論!!!!!
?
?
以下代碼:(百度之星比賽有代碼長度的分數,變量為了簡介并不與上面題解中一一對應,望諒解)
bug修改版
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<map> 7 using namespace std; 8 const int N=310; 9 map<int,bool>mp; 10 int T,C,n,m,a[N],s[N],x; 11 char q[N][N]; 12 int dfs(int x,int y){ 13 char &ans=q[x][y]; 14 if(ans>=0) return ans; 15 if(y-x<0) return ans=1; 16 if(y==x) return ans=0; 17 if(y-x==1) return ans=mp[a[y]-a[x]]; 18 if(y-x==2) return ans=(a[y]-a[y-1]==a[y-1]-a[x] && mp[a[y-1]-a[x]]); 19 ans=0; 20 for(int i=x+1;i<y && !ans;i++){ 21 if(dfs(x,i) && dfs(i+1,y))ans=1; 22 if(a[i]-a[x] == a[y]-a[i] && dfs(x+1,i-1) && dfs(i+1,y-1) && mp[a[i]-a[x]]) 23 ans=1; 24 } 25 if(ans) return ans; 26 if(dfs(x+1,y-1) && mp[a[y]-a[x]]) ans=1; 27 return ans; 28 } 29 int main() 30 { 31 scanf("%d",&T); 32 while(T--){ 33 printf("Case %d: ",++C); 34 scanf("%d%d",&n,&m); 35 mp.clear(); 36 for(int i=0;i<n;i++)scanf("%d",a+i); 37 for(int i=0;i<m;i++){ 38 scanf("%d",&x); 39 mp[x]=true; 40 } 41 if(n==0){ 42 puts("0"); 43 continue; 44 } 45 memset(q,-1,sizeof q); 46 for(int i=1;i<n;i++){ 47 if(dfs(0,i)){ 48 s[i]=i+1; 49 continue; 50 } 51 s[i]=s[i-1]; 52 for(int j=1;j<i;j++)if(dfs(j,i) && s[j-1]+i-j+1>s[i]) 53 s[i]=s[j-1]+i-j+1; 54 } 55 printf("%d\n",s[n-1]); 56 } 57 return 0; 58 }?
posted on 2012-12-24 18:27 混沌DM 閱讀(...) 評論(...) 編輯 收藏轉載于:https://www.cnblogs.com/hundundm/archive/2012/12/24/2831444.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的2012百度之星冬季赛第二场第二题 消去游戏I的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 不使用GACUtil.exe,如何部署和
- 下一篇: 电信运营商物联网实践建议