• P1005 矩阵取数游戏 (60)


    DP

    题目链接:https://www.luogu.org/problem/show?pid=1005

    题目大意:

    有一个n * m的矩阵,玩一个取数游戏,每次从1到n行取行首或行尾乘2的

    取的次数次方为这次取数的得分,求得分最大值。

     

    A:每次取每行最大不就行了?定义f[i][j]为取到第i次,到第j行的max。

     

        for(int i = 1;i <= m;++ i){
            for(int j = 1;j <= n;++ j){
                f[i][j] = maxx(f[i][j - 1] * a[j][s],f[i][j - 1] * a[j][e]);
            }
        }

     

    B:还有个次数次方呢...

    A:那每次取最小

    B:你怎么知道后面一定会更优?

    A:……那怎么办?

    B:为什么不可以用你说的方法呢?

    A:因为我这个方法不能保证这个状态下是最优的。

    B:因为不论取左边还是取右边都会影响后面的状态。

    B:这个问题熟不熟悉?

    A:嗯,方格取数!!当时也是分开走会影响后面的状态!!

    B:怎么处理的来着?

    A:加维度!!

    END.

    这个题第一眼看上去感觉好简单,

    然后发现思路明明是贪心啊QAQQ,

    然后感觉好难,

    但是不要紧,一点点分析,把大问题转化成小问题,

    比如这个题,

    要取m次,先看取1次?要取n行,先看取1行?要取1行,先看只有1个,2个,3个数的方法?

    1个就不说了QAQQ,

    如果有两个数,a b为一行,

    如果取走b剩下a,就加上b * ...直接和另一种情况取min

    如果有3个数 a b c,

    可以取走a or c,剩下b,

    即b之前的状态可能是a b 或是 b c,

    这两种情况取走a或c都有可能成为答案,那就都记下来,

    4个数 a b c d,一样的,

    在更新剩下b c的状态的时候,

    他之前的状态也有两种:a b c或者 b c d,

    两种情况取走a或取走d也都可以成为答案,都记录,

    咦?有没有发现a b c的情况我们之前明明已经更新过了?(取3个数的时候)

    也就是说,b c在所在的位置,所组成的区间,我们已经更新过了?

    可以记忆化,那一定可以dp,

    那怎么设计状态呢?

    要存一个当前取的次数,当前取到哪一行,还有??

    看上面上面上面!!不是已经知道b c更新过了么?

    也就是说怎么存b c这段区间呢?

    存一段区间可以存起点终点或者起点长度,

    随便选一个好了,

    设计状态:f[i][j][s][e]:第i次取,到第j行,s到e区间的答案

    Codes:

     

        f[i][j][s][e] = maxx(f[i][j][s - 1][e] + a[j][s - 1] * (1 << i),f[i][j][s][e + 1] + a[j][e + 1] * (1 << i));

     

    哇哦4维啊好长,有没有发现我在用 i 更新 i ?在用 j 更新 j ?

    扔掉扔掉,i可以边输入边处理(这里就不写了)

    这样状态定义可以是这样;

    f[i][s][e]:到第i行,s到e区间的答案。

    就是这样啦~

           for(int i = 1;i <= n;++ i){
             for(int s = 1;s <= m;++ s){
                 for(int e = m;e >= s;-- e){
                      int t = m - (e - s) - 1;
                      f[i][s][e] = maxx(f[i][s - 1][e] + a[i][s - 1] * (1 << t),
                                      f[i][s][e + 1] + a[i][e + 1] * (1 << t));
                }
            }
        }

    (其中t是第t次取数,可以通过s和e来求)

    大神还有一种写法:

      for(int i = 1;i <= n;i ++)
             for(int j = 1;j <= m;j ++)
                  for(int k = m;k >= j;k --){
                      int t = m - (k - j);
                      dp[i][j + 1][k] = max(dp[i][j + 1][k],dp[i][j][k] + map[i][j] * (1 << t));
                      dp[i][j][k - 1] = max(dp[i][j][k - 1],dp[i][j][k] + map[i][k] * (1 << t));
                  }

    然后最终答案很明显是每行答案之和啦~(当时我这里写错QAQQ忘了统计。。。)

    Codes:

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<algorithm>
     5 #define ll long long
     6 using namespace std;
     7 const int N = 80 + 5;
     8 ll a[N][N],f[N][N][N],n,m;
     9 ll maxx(ll x,ll y){if(x > y) return x;return y;}
    10 int main(){
    11     scanf("%d%d",&n,&m);
    12     for(int i = 1;i <= n;++ i){
    13         for(int j = 1;j <= m;++ j){
    14             scanf("%d",&a[i][j]);
    15         }
    16     }
    17     for(int i = 1;i <= n;++ i){
    18         for(int s = 1;s <= m;++ s){
    19             for(int e = m;e >= s;-- e){
    20                 int t = m - (e - s) - 1;
    21                 f[i][s][e] = maxx(f[i][s - 1][e] + a[i][s - 1] * (1 << t),f[i][s][e + 1] + a[i][e + 1] * (1 << t));
    22             }
    23         }
    24     }
    25     ll ans = 0;
    26     for(int i = 1;i <= n;++ i){
    27         ll ans1 = 0;
    28         for(int j = 1;j <= m;++ j)
    29             ans1 = maxx(ans1,f[i][j][j] + a[i][j] * (1 << m));
    30         ans += ans1;
    31     }
    32     cout << ans << '
    ';
    33     return 0;
    34 }

     还有一种状态定义的方式f[i][j][a][b] :

    表示在第i次,取到第j行,左边取了a个,右边取了b个的max,

    这个更加直接应该更容易想到,(虽然我没有想到QAQQ)

    左边取a个上一个状态是左边取了a - 1个,右边同理。

    i,j同样可以去掉。

    注意数据范围,需要用高精,这里我没有加。60。

    这个题就是这样啦。

    MAS:

    先知道怎样递推再定义状态

    保证状态的唯一性

    不会写就加维度加维度加维度

  • 相关阅读:
    js 实现树效果
    (转)JavaScript 冒泡实例与阻止冒泡方法
    (转)js全页面刷新方法
    js 右键菜单
    (转)js,jQuery屏蔽鼠标右与jquery 鼠标右键事件、左键单击事件判定
    Flex取xml文件中的值
    oracle相关时间计算,得到季度第一天、最后一天
    (转)ASP.NET MVC VS2010中更改默认调试浏览器
    JS 与 后台如何获取 Cookies
    js 弹出新页面
  • 原文地址:https://www.cnblogs.com/Loizbq/p/7742366.html
Copyright © 2020-2023  润新知