JZOJ 1322. 硬币游戏
Description
FJ的奶牛喜歡玩硬幣游戲,所以FJ發明了一個新的硬幣游戲。一開始有N(5<=N<=2,000)個硬幣堆成一疊,從上往下數第i個硬幣有一個整數值C_i(1<=C_i<=100,000)。
兩個玩家輪流從上倒下取硬幣,玩家1先取,可以從上面取1個或2個硬幣,下一輪的玩家可以取的硬幣數量最少為1個,最多為上一個玩家取的數量的2倍,硬幣全部取完比賽結束。
已知玩家2絕頂聰明,會采用最優策略,現在請你幫助玩家1,使得玩家1取得的硬幣值的和最大。
Input
第一行輸入N
第二至N+1行每行輸入一個整數C_i
Output
輸出玩家1能獲得的最大值。
Sample Input
5
1
3
1
7
2
Sample Output
9
Solution
這題顯然是一道博弈題(“絕頂聰明”),但是傳統的搜索過不了這么大的極限數據。
觀察到目標是求極值,于是我們考慮動態規劃。
設 F[i][j] 表示還剩余 i 個硬幣、上一次對手選了 j 個硬幣的最大獲利。
再設 sum[i][j] 表示從 i 到 j 的價值和(可以用前綴和維護)。
那么可得轉移方程式:
F[i][j]=max{sum[i?k+1][i]+(sum[1][i?k]?F[i?k][k])}其中:
- sum[i?k+1][i] 表示本次自己拿走第 i 枚硬幣的錢數和;
- sum[1][i?k]?f[i?k][k] 表示在剩下的局面中自己還能拿的錢數和;
- 1≤k≤min(2?k,i) 。
于是我們將上式化簡,得:
F[i][j]=max{sum[1][i]?F[i?k][k]}?(1≤k≤min(2?k,i))但然而這樣的 DP 是 O(N3) 的,極限數據會時間超限,要想辦法優化成 O(N2) 的。
繼續觀察上式,發現兩個相鄰狀態 F[i][j] 和 F[i][j?1] 有很多重復的轉移,
代入可以發現只有兩個狀態是有用的,即:當 k=2?j 或 k=2?j?1 時有意義。
那么只轉移兩個狀態,時間復雜度就是 O(N2) ,成功通過本題。
Code
#include<cstdio> using namespace std; const int N=2001; int sum[N],f[N][N]; inline int read() {int X=0,w=1; char ch=0;while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();return X*w; } inline int max(int x,int y) {return x>y?x:y; } int main() {int n=read();for(int i=1;i<=n;i++) sum[n-i+1]=read();for(int i=1;i<=n;i++) sum[i]+=sum[i-1];for(int i=1;i<=n;i++)for(int j=1;j<=n-i+1;j++){/*for(int k=1;k<=2*j && k<=i;k++)f[i][j]=max(f[i][j],sum[i]-f[i-k][k]); N^3做法 */f[i][j]=f[i][j-1];int k=2*j-1;if(i>=k) f[i][j]=max(f[i][j],sum[i]-f[i-k][k]);if(i>=++k) f[i][j]=max(f[i][j],sum[i]-f[i-k][k]);}printf("%d",f[n][1]);return 0; }總結
以上是生活随笔為你收集整理的JZOJ 1322. 硬币游戏的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JZOJ 1321. 灯
- 下一篇: JZOJ 1277. 最高的奶牛