noip2007 矩阵游戏

题面

题目描述
帅帅经常更同学玩一个矩阵取数游戏:对于一个给定的
n∗m
n∗m
的矩阵,矩阵中的每个元素a[i][j]据为非负整数。游戏规则如下:
  1. 每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有的元素;
  2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
  3. 每次取数都有一个得分值,为每行取数的得分之和;每行取数的得分 = 被取走的元素值*2^i,其中i表示第i次取数(从1开始编号);
游戏结束总得分为m次取数得分之和。
  帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。
输入格式
包括n+1行;
第一行为两个用空格隔开的整数n和m。
第2~n+1行为n*m矩阵,其中每行有m个用单个空格隔开
输出格式
仅包含1行,为一个整数,即输入矩阵取数后的最大的分。

题解

区间dp水题。
每一行做一次区间dp。
然后注意取数的权值处理问题,也就是当前数是第几个取。
最小的区间f[j][j]是最后取得,权值是 2 m
状态:

f [ H ] [ i ] [ j ] H [ i , j ]

对于一个区间[i,j],最先取的数可能是a[i]或者a[j]。
所以状态转移方程是:
f [ H ] [ i ] [ j ] = m a x ( f [ H ] [ i ] [ j 1 ] + a [ j ] 2 m l e n + 1 , f [ H ] [ i + 1 ] [ j ] + a [ i ] 2 m l e n + 1 )

这里有一个骚操作——__int128 请自行百度

code

#include<bits/stdc++.h>
using namespace std;
typedef __int128 ll;
inline ll read(){
    ll num = 0;
    char c = ' ';
    bool flag = true;
    for(;c > '9' || c < '0';c = getchar())
    if(c == '-')
    flag = false;
    for(;c >= '0' && c <= '9';num = num*10+c-48,c=getchar());
    return flag ? num : -num;
}
void print(ll a){
    if(a>9)print(a/10);
    putchar(a%10+'0');
}
const ll maxn=90;
ll f[maxn][maxn][maxn];
ll n,m,a[maxn][maxn];
ll qpow[maxn];//存放2的幂。
void init(){
    n=read();m=read();
    qpow[0]=1;
    for(int i=1;i<=m;i++)
        qpow[i]=qpow[i-1]*2;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            a[i][j]=read();
            f[i][j][j]=a[i][j]*qpow[m];
        }
}
void DP(){
    for(int H=1;H<=n;H++){
        for(int len=2;len<=m;len++)
            for(int i=1;i+len-1<=m;i++){
                int j=i+len-1;
                f[H][i][j]=max(f[H][i][j],f[H][i+1][j]+a[H][i]*qpow[m-len+1]);
                f[H][i][j]=max(f[H][i][j],f[H][i][j-1]+a[H][j]*qpow[m-len+1]);
            }
    }
    ll ans=0;
    for(int i=1;i<=n;i++)
        ans=ans+f[i][1][m];
    print(ans);
}
int main(){
    init();
    DP();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39670434/article/details/80144905