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;}}
  • 相关阅读:
    Mac修改默认的电子邮箱客户端
    记录下生活:ETC卡充值(上海)
    Mac下卸载Mysql数据库
    网络爬虫学习笔记 1
    Clock置换算法
    用random.randint函数时 报错 'str' object cannot be interpreted as an integer问题
    分页式存储管理
    初学C++ vector 容器
    马一个讲devc++调试讲的很好的视频
    一些笔记(xss 跨站脚本攻击)
  • 原文地址:https://www.cnblogs.com/Crazy01/p/7665880.html
Copyright © 2011-2022 走看看