1. 程式人生 > >洛谷P1005 矩陣取數遊戲【記憶化搜尋】

洛谷P1005 矩陣取數遊戲【記憶化搜尋】

題目描述

帥帥經常跟同學玩一個矩陣取數遊戲:對於一個給定的n×mn \times m的矩陣,矩陣中的每個元素ai,ja_{i,j} 均為非負整數。遊戲規則如下:

每次取數時須從每行各取走一個元素,共nn個。經過mm次後取完矩陣內所有元素; 每次取走的各個元素只能是該元素所在行的行首或行尾; 每次取數都有一個得分值,為每行取數的得分之和,每行取數的得分 = 被取走的元素值×2i\times 2^i ,其中ii表示第i次取數(從1開始編號); 遊戲結束總得分為m次取數得分之和。 帥帥想請你幫忙寫一個程式,對於任意矩陣,可以求出取數後的最大得分。

輸入格式:

輸入檔案包括n+1行:

第1行為兩個用空格隔開的整數n和m

第2~n+1行為n×m矩陣,其中每行有m個用單個空格隔開的非負整數。

輸出格式:

輸出檔案僅包含1行,為一個整數,即輸入矩陣取數後的最大得分。

說明

資料範圍: 60%的資料滿足:1n,m301≤n,m≤30,答案不超過101610^{16} 100%的資料滿足:1n,m800ai,j10001≤n,m≤80,0 \le a_{i,j} \le 1000

題目分析

被高精度活活卡死的記搜大水題 試著把取數方式看作 在每一行按一定的順序依次取完m個數,總共n行所以取n次 顯然與原來的方式是等價的

那麼我們可以依次計算每一行取數總分最大值

,把n行的最大值相加作為答案 假設在某一行內 dp[i][ll][rr]dp[i][ll][rr]表示ii輪,當前隊首為llll隊尾為rrrr取到最後能得的最大分數

狀態轉移方程 dp[k][ll][rr]=max(DP(k+1,ll+1,rr)+a[ll]2i,DP(k+1,ll,rr1)+a[rr]2i);dp[k][ll][rr]=max(DP(k+1,ll+1,rr)+a[ll]*2^i,DP(k+1,ll,rr-1)+a[rr]*2^i);

ll,rr1)+a[rr]2i); 加上記憶化即可

活活被卡死在高精度,實所無奈直接用了__int128

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
typedef __int128 lt;

lt read()
{
    lt f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

void print(lt x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9)print(x/10);
    putchar(x%10+'0');
}

const int maxn=90;
int n,m;
lt a[maxn][maxn];
lt dp[maxn][maxn][maxn],ans;

lt qpow(lt a,lt k)
{
    lt res=1;
    while(k>0){
        if(k&1) res*=a;
        a*=a; k>>=1;
    }
    return res;
}

lt DP(lt k,int ll,int rr,int d)
{
    if(dp[k][ll][rr]) return dp[k][ll][rr];
    lt tpow=qpow(2,k);
    if(ll==rr) return a[d][ll]*tpow;
    return dp[k][ll][rr]=max(DP(k+1,ll+1,rr,d)+a[d][ll]*tpow,DP(k+1,ll,rr-1,d)+a[d][rr]*tpow);
}

int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)
    for(int j=1;j<=m;++j)
    a[i][j]=read();
    
    for(int i=1;i<=n;++i)
    {
    	memset(dp,0,sizeof(dp));
        ans+=DP(1,1,m,i);
    }
    print(ans);
    return 0;
}