UVA-10891 Game of Sum
2017-10-14 10:22
429 查看
题目大意:
俩小孩玩游戏, 轮流从左边或者右边选择连续若干个数( 除非全选, 否则只能选一边 ), 它们的和就是这个小孩这一次的得分, 然后删去选择的数, 问先手的小孩最多可以比后手的多多少分.
很明显的dp题是不是?
那么怎么设状态呢? dp[ i ][ j ]表示在[ l , r ]这一段区间内, 先手可以得到的最大分数. 那么怎么转移呢? 想来想去也没有什么想法.
这时候注意到一个地方: 每次只能选左边或者右边的若干个数, 那么假设现在剩下的是[ l , r ]这一段的数, 先手选择了[ l , k ], 剩下的就是[ k+1 , r ]; 先手选择[ k , r ], 剩下的就是[ l , k-1 ].
而剩下的又是一个类似的子问题了. 所以必定是从dp[ l ][ k ]和dp[ k ][ r ]转移( 其中k∈[ l , r ] ).
考虑到状态是先手的最大分数, 而这一轮先手选择之后, 后手变先手, 那么原后手也是按照最优策略选择, 所以也是枚举了子区间转移.
由于存在先后手转换, 而且二者都是按照最优策略, 所以当前先手肯定枚举k, 在两种方向( 选右边或者选左边 )中选择子区间最优策略下自己剩的分最多的方案.
而给自己能够剩的分是sum[ l ][ k ]-dp[ l ][ k ]或者sum[ k ][ j ]-dp[ k ][ j ].
这样转移就很明显了, 记得当前选择之后也有选择的分, 也要加入转移取max:
dp[ i ][ j ]=max( sum[ l ][ k ]-dp[ l ][ k ]+sum[ k+1 ][ r ] , sum[ k ][ r ]-dp[ k ][ r ]+sum[ l ][ k-1 ] ).
化简一下就是:
dp[ i ][ j ]=max( -dp[ l ][ k ] , -dp[ k ][ r ] )+sum[ l ][ r ].
sum用前缀和搞一下, 枚举一下k, 注意初状态为0和多组数据, 剩下的就是没有什么技术含量的码代码了.
顺带说一句, 这一题是我人生压行的巅峰, 上面那个代码被我压成了这个鬼样子( 除开made by Crazy01只有323B, 只是为了争夺vjudge上这一题的rank 1....):
俩小孩玩游戏, 轮流从左边或者右边选择连续若干个数( 除非全选, 否则只能选一边 ), 它们的和就是这个小孩这一次的得分, 然后删去选择的数, 问先手的小孩最多可以比后手的多多少分.
很明显的dp题是不是?
那么怎么设状态呢? dp[ i ][ j ]表示在[ l , r ]这一段区间内, 先手可以得到的最大分数. 那么怎么转移呢? 想来想去也没有什么想法.
这时候注意到一个地方: 每次只能选左边或者右边的若干个数, 那么假设现在剩下的是[ l , r ]这一段的数, 先手选择了[ l , k ], 剩下的就是[ k+1 , r ]; 先手选择[ k , r ], 剩下的就是[ l , k-1 ].
而剩下的又是一个类似的子问题了. 所以必定是从dp[ l ][ k ]和dp[ k ][ r ]转移( 其中k∈[ l , r ] ).
考虑到状态是先手的最大分数, 而这一轮先手选择之后, 后手变先手, 那么原后手也是按照最优策略选择, 所以也是枚举了子区间转移.
由于存在先后手转换, 而且二者都是按照最优策略, 所以当前先手肯定枚举k, 在两种方向( 选右边或者选左边 )中选择子区间最优策略下自己剩的分最多的方案.
而给自己能够剩的分是sum[ l ][ k ]-dp[ l ][ k ]或者sum[ k ][ j ]-dp[ k ][ j ].
这样转移就很明显了, 记得当前选择之后也有选择的分, 也要加入转移取max:
dp[ i ][ j ]=max( sum[ l ][ k ]-dp[ l ][ k ]+sum[ k+1 ][ r ] , sum[ k ][ r ]-dp[ k ][ r ]+sum[ l ][ k-1 ] ).
化简一下就是:
dp[ i ][ j ]=max( -dp[ l ][ k ] , -dp[ k ][ r ] )+sum[ l ][ r ].
sum用前缀和搞一下, 枚举一下k, 注意初状态为0和多组数据, 剩下的就是没有什么技术含量的码代码了.
//made by Crazy01 #include<queue> #include<math.h> #include<stdio.h> #include<string.h> #include<stdlib.h> #include<iostream> #include<algorithm> #define inf 1<<30 #define ll long long #define db double #define c233 cout<<"233"<<endl #define mem(s) memset(s,0,sizeof(s)) #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) const int N=105; using namespace std; int dp ,sum ,a ; int n; inline int gi(){ int x=0,res=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();} while(ch<='9'&&ch>='0')x=(x<<1)+(x<<3)+ch-48,ch=getchar(); return x*res; } void init(){ n=gi(); if(!n)exit(0); for(int i=1;i<=n;i++) a[i]=gi(),sum[i]=sum[i-1]+a[i]; } void work(){ for(int det=1;det<=n;det++) for(int i=1;i+det-1<=n;i++){ if(det==1){dp[i][i]=a[i]; continue;} int j=i+det-1; dp[i][j]=-inf; for(int k=i;k<j;k++) dp[i][j]=max(dp[i][j],-dp[i][k]); for(int k=j;k>i;k--) dp[i][j]=max(dp[i][j],-dp[k][j]); dp[i][j]=max(dp[i][j],0); dp[i][j]+=sum[j]-sum[i-1]; } printf("%d\n",dp[1] -sum +dp[1] ); } int main(){ while(1){ init(); work(); } return 0; }
顺带说一句, 这一题是我人生压行的巅峰, 上面那个代码被我压成了这个鬼样子( 除开made by Crazy01只有323B, 只是为了争夺vjudge上这一题的rank 1....):
//made by Crazy01 #include<iostream> #define P f[i][j] using namespace std;int f[105][105],s[105],n,i,w,k,j;int main(){cin>>n;while(n){for(i=1;i<=n;i++)cin>>s[i],s[i]+=s[i-1];for(w=0;w<n;w++)for(i=1;i+w<=n;i++){j=i+w;P=0;for(k=i;k<j;k++)P=max(P,-f[i][k]);for(k=j;k>i;k--)P=max(P,-f[k][j]);P+=s[j]-s[i-1];}cout<<f[1] *2-s <<endl;cin>>n;}}
相关文章推荐
- UVA 10891 Game of Sum(区间DP)
- UVA-10891 - Game of Sum
- UVa 10891 Game of Sum (区间DP&博弈)
- UVA - 10891 Game of Sum(区间dp)
- UVA 10891 Game of Sum(区间DP)
- Game of Sum - UVa 10891 dp
- Uva - 10891 - Game of Sum
- UVa10891 Game of Sum(dp)
- UVA 10891 Game of Sum
- UVA 10891 Game of Sum
- UVA 10891 Game of Sum(区间DP)
- UVA - 10891 Game of Sum
- uva 10891 - Game of Sum
- UVA 10891 Game of Sum(区间DP)
- Uva 10891 - Game of Sum ( 区间dp )
- uva 10891 game of sum
- UVA 10891 Game of Sum(区间DP)
- uva 10891 - Game of Sum
- UVA 10891-Game of Sum(基础DP)
- UVa 10891 Game of Sum(博弈区间DP)