JZOJ 1322. 硬币游戏
Description
FJ的奶牛喜歡玩硬幣游戲,所以FJ發(fā)明了一個(gè)新的硬幣游戲。一開始有N(5<=N<=2,000)個(gè)硬幣堆成一疊,從上往下數(shù)第i個(gè)硬幣有一個(gè)整數(shù)值C_i(1<=C_i<=100,000)。
兩個(gè)玩家輪流從上倒下取硬幣,玩家1先取,可以從上面取1個(gè)或2個(gè)硬幣,下一輪的玩家可以取的硬幣數(shù)量最少為1個(gè),最多為上一個(gè)玩家取的數(shù)量的2倍,硬幣全部取完比賽結(jié)束。
已知玩家2絕頂聰明,會(huì)采用最優(yōu)策略,現(xiàn)在請(qǐng)你幫助玩家1,使得玩家1取得的硬幣值的和最大。
Input
第一行輸入N
第二至N+1行每行輸入一個(gè)整數(shù)C_i
Output
輸出玩家1能獲得的最大值。
Sample Input
5
1
3
1
7
2
Sample Output
9
Solution
這題顯然是一道博弈題(“絕頂聰明”),但是傳統(tǒng)的搜索過不了這么大的極限數(shù)據(jù)。
觀察到目標(biāo)是求極值,于是我們考慮動(dòng)態(tài)規(guī)劃。
設(shè) F[i][j] 表示還剩余 i 個(gè)硬幣、上一次對(duì)手選了 j 個(gè)硬幣的最大獲利。
再設(shè) sum[i][j] 表示從 i 到 j 的價(jià)值和(可以用前綴和維護(hù))。
那么可得轉(zhuǎn)移方程式:
F[i][j]=max{sum[i?k+1][i]+(sum[1][i?k]?F[i?k][k])}其中:
- sum[i?k+1][i] 表示本次自己拿走第 i 枚硬幣的錢數(shù)和;
- sum[1][i?k]?f[i?k][k] 表示在剩下的局面中自己還能拿的錢數(shù)和;
- 1≤k≤min(2?k,i) 。
于是我們將上式化簡(jiǎn),得:
F[i][j]=max{sum[1][i]?F[i?k][k]}?(1≤k≤min(2?k,i))但然而這樣的 DP 是 O(N3) 的,極限數(shù)據(jù)會(huì)時(shí)間超限,要想辦法優(yōu)化成 O(N2) 的。
繼續(xù)觀察上式,發(fā)現(xiàn)兩個(gè)相鄰狀態(tài) F[i][j] 和 F[i][j?1] 有很多重復(fù)的轉(zhuǎn)移,
代入可以發(fā)現(xiàn)只有兩個(gè)狀態(tài)是有用的,即:當(dāng) k=2?j 或 k=2?j?1 時(shí)有意義。
那么只轉(zhuǎn)移兩個(gè)狀態(tài),時(shí)間復(fù)雜度就是 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; }總結(jié)
以上是生活随笔為你收集整理的JZOJ 1322. 硬币游戏的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JZOJ 1321. 灯
- 下一篇: JZOJ 1277. 最高的奶牛