zoukankan      html  css  js  c++  java
  • UVA-10891 Game of Sum

    题目大意:

    俩小孩玩游戏, 轮流从左边或者右边选择连续若干个数( 除非全选, 否则只能选一边 ), 它们的和就是这个小孩这一次的得分, 然后删去选择的数, 问先手的小孩最多可以比后手的多多少分. 

    很明显的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[N][N],sum[N],a[N];
    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
    ",dp[1][n]-sum[n]+dp[1][n]);
    }
    
    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][n]*2-s[n]<<endl;cin>>n;}}
  • 相关阅读:
    JavaScript 为字符串添加样式 【每日一段代码80】
    JavaScript replace()方法 【每日一段代码83】
    JavaScript for in 遍历数组 【每日一段代码89】
    JavaScript 创建用于对象的模板【每日一段代码78】
    html5 css3 新元素简单页面布局
    JavaScript Array() 数组 【每日一段代码88】
    JavaScript toUTCString() 方法 【每日一段代码86】
    位运算
    POJ 3259 Wormholes
    POJ 3169 Layout
  • 原文地址:https://www.cnblogs.com/Crazy01/p/7665880.html
Copyright © 2011-2022 走看看