zoukankan      html  css  js  c++  java
  • NOIP2007 矩阵取数游戏(区间DP)

    传送门

    这道题第一眼看上去可能让人以为是贪心……不过贪心并不行,因为每次的操作是有2的幂次方的权值的。这样的话直接每次贪心最小的就目光短浅。所以那我们自然想到了DP。

    据说这是一道很正常的区间DP?

    区间DP的基本思想,就是先处理出小区间的最优解,再由多个小区间合并成一个大区间。

    不过这道题的想法略微有些不同。首先从题目描述上来看,每行的取数是独立的,对于每一行我们来分析一下。

    首先,因为题目中说只能取一行元素当前的首个元素或者末尾元素。既然如此,我们假设dp[i][j]表示选取区间i~j所能获得的最大值。

    这样的话,dp[i][j]就只能从两方面转移过来。一是dp[i+1][j],二是dp[i][j-1].这样的话,我们考虑一下转移时候的状态。因为每次转移所获的的分数是当前选取的数值乘以2^选举的次数,所以我们可以这么想,对于一个内部的区间,它在被转移的时候,本身是要被*2的。之后这种状态就会被继续跟随着*2,所以选到最后必然是符合题意的。

    所以说DP的方程就是dp[i][j] = min(dp[i+1][j]*2 + a[i] * 2,dp[i][j-1]*2 + a[j]*2);

    DP方程说完了,之后说该怎么DP。我们知道区间DP的思想是先算小区间,之后合并成大区间,所以我们可以从0~n-1枚举区间长度,之后枚举区间的左右端点进行转移。

    还有就是这道题要使用高精度……不过可以选择自己写一个高精乘,高精加,和高精度比较的struct,直接封装起来。这样就可以了。

    看一下代码(因为高精模板是抄的……高精减可以忽略……)

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define rep(i,a,n) for(ll i = a;i <= n;i++)
    #define per(i,n,a) for(ll i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    const int M = 105;
    typedef long long ll;
    
    int read()
    {
        int ans = 0,op = 1;
        char ch = getchar();
        while(ch < '0' || ch > '9')
        {
            if(ch == '-') op = -1;
            ch = getchar();
        }
        while(ch >= '0' && ch <= '9')
        {
            ans *= 10;
            ans += ch - '0';
            ch = getchar();
        }
        return ans * op;
    }
    
    struct big
    {
        int len,num[50];
        big()
        {
            len = 0;
            memset(num,0,sizeof(num));
        }
        big(int p)
        {
            len = 0;
            while(p) num[++len] = p % 10,p /= 10;
        }
        void init(int p)
        {
            len = 0;
            while(p) num[++len] = p % 10,p /= 10;
        }
        big operator + (const big &g) const
        {
            big ans;
            int s = max(len,g.len);
            ans.len = s;
            rep(i,1,s)
            {
                ans.num[i] += num[i] + g.num[i]; 
                if(ans.num[i] >= 10) ans.num[i] -= 10,ans.num[i+1]++;
            }
            if(ans.num[s+1]) ans.len++;
            return ans;
        }
        big operator - (const big &g) const
        {
            big ans;
            int s = max(len,g.len);
            ans.len = s;
            rep(i,1,s) ans.num[i] = num[i] - g.num[i];
            rep(i,1,s) if(ans.num[i] < 0) ans.num[i+1]--,ans.num[i] += 10;
            if(ans.num[s+1] != 0) ans.len++;
            return ans;
        }
        big operator * (const big &g) const
        {
            big ans;
            int s1 = len,s2 = g.len;
            rep(i,1,s1)
            rep(j,1,s2) ans.num[i+j-1] += num[i] * g.num[j];
            int s = s1 + s2 - 1,k = 1;
            while(ans.num[k] || k <= s)
            {
                ans.num[k+1] += ans.num[k] / 10;
                ans.num[k] = ans.num[k] % 10;
                k++;
            }
            if(!ans.num[k]) k--;
            ans.len = k;
            return ans;
        }
        friend big bmax(const big &f,const big &g)
        {
            if(f.len < g.len) return g;
            else if(f.len > g.len) return f;
            else
            {
                per(i,f.len,1) 
                {
                    if(f.num[i] < g.num[i]) return g;
                    else if(f.num[i] > g.num[i]) return f;
                }
            }
            return f;
        }
        void out()
        {
            if(!len) printf("0
    ");
            per(i,len,1) printf("%d",num[i]);enter;
        }
    };
    big a,b,c,f[81][81],dp[81][81][81],ans,pow2[81],now;
    
    int n,m;
    
    int main()
    {
        n = read(),m = read();
        pow2[1].init(2);
        rep(i,2,m) pow2[i] = pow2[i-1] * pow2[1];
        rep(i,1,n)
        rep(j,1,m) f[i][j].init(read());
        rep(i,1,n)
        {
            rep(p,0,m)
            rep(q,1,m-p) 
            {
                dp[i][q][p+q] = bmax((dp[i][q+1][p+q] * pow2[1] + pow2[1] * f[i][q]),(dp[i][q][p+q-1] * pow2[1] + pow2[1] * f[i][p+q]));//注意第一维只是起计数作用,无实际意义。
    //            now = dp[i][q][p+q],now.out();
            }
            ans = ans + dp[i][1][m];
        }
        ans.out(); 
        return 0;
    }
  • 相关阅读:
    2020.2.14
    2020.2.13
    规划极限编程阅读笔记03
    学习进度——第十六周
    JSP跳转到Servlet的两种配置
    规划极限编程阅读笔记02
    规划极限编程阅读笔记01
    单词接龙
    学习进度——第十五周
    输入法评价
  • 原文地址:https://www.cnblogs.com/captain1/p/9538145.html
Copyright © 2011-2022 走看看