Luogu P1005 [NOIP2007 提高组] 矩阵取数游戏

题目传送门

题目描述

帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的 n \times m 的矩阵(1\le n,m\le 80),矩阵中的每个元素a_{i,j}均为非负整数(0\le a_{i,j}\le1000)。游戏规则(青春精简畅享SE版​​​​​)如下:

每次从每行的首或尾取一个数,得分为a_{i,j}\times 2^i,其中i为第i次取数(从1开始计数),求可取得的最大值。

### 输入格式

输入文件包括 n+1 行:

第一行为两个用空格隔开的整数 n 和 m

2\sim n+1 行为 n \times m 矩阵,其中每行有 m 个用单个空格隔开的非负整数。

样例输入 #1

2 3
1 2 3
3 4 2

样例输出 #1

82

分析

首先,因为我们是每行每次都要取一个数,所以此题实际上只用考虑每一行的最优解后再逐行相加即可。

想到这一点后,这道题(的dp部分)就不是很难了。我们可以令dp_{i,j}为此行中ij可取得的最大值,然后我们可以得出以下的状态转移方程(pw数组为2的整数次幂):

dp[i][j]=max(dp[i-1][j]+pw[m-j+i-1]*a[i-1],dp[i][j+1]+pw[m-j+i-1]*a[j+1]);

这个状态转移方程相信大家可以大概可以理解,由于题目所说只可选择每行的第一个或最后一个数,所以我们只要取首(dp_{i-1,j})尾(dp_{i,j+1})再加上此处得分的最大值就可以了。

最后给出代damn码:

#include<bits/stdc++.h>
#define int long long
#define endl '\n' 
using namespace std;
int n,m,ans;
int dp[105][105],a[105],pw[105];
signed main(){
    cin>>n>>m;
    for(int i=1;i<=80;i++)pw[i]=pow(2,i);//记录二的整数次幂方便后面的计算 
    for(int t=1;t<=n;t++){
        memset(dp,0,sizeof(dp));//十年OI一场空 
        for(int i=1;i<=m;i++)cin>>a[i];
        for(int i=1;i<=m;i++){
            for(int j=m;j>=i;j--){
                dp[i][j]=max(dp[i-1][j]+pw[m-j+i-1]*a[i-1],dp[i][j+1]+pw[m-j+i-1]*a[j+1]);
            } 
        } 
        int maxh=0;
        for(int i=1;i<=m;i++)maxh=max(maxh,dp[i][i]+pw[m]*a[i]);//找出每行的最优解 
        ans+=maxh;
    }
    cout<<ans<<endl;
    return 0;//十年OI两场空 
}//完结撒花,感谢陪伴 


盲生,你发现了华点。提交这个代码只能得60分,这时,我们再仔细看看数据:每行取数的得分 = 被取走的元素值\times 2^i1\le n,m\le 800\le a_{i,j}\le1000,最多要计算到2^{80},所以现有的unsigned long long是无法解决这个问题的,摆在我们眼前的是两条路:高精度和__int128。
 
 为了掌握更先进的技术(懒得写高精度),我们选择用__int128继续完成此题。
 
 最后是AC(迫真)代Damn码:
 
 

#include<bits/stdc++.h>
#define endl '\n' 
using namespace std;
__int128 n,m,ans;//泰裤辣! 
__int128 dp[105][105],a[105],pw[105];
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
inline void print(__int128 x){//太高♂级♂辣
    if(x<0){
        putchar('-');
        x=-x;
    }
    if(x>9)print(x/10);
    putchar(x%10+'0');
}
signed main(){
    n=read();m=read();//由于__int128不在c++标准内,只有四则运算功能,所以需要配合快读快写食用 
    for(int i=1;i<=80;i++)pw[i]=pow(2,i);//记录二的整数次幂方便后面的计算 
    for(int t=1;t<=n;t++){
        memset(dp,0,sizeof(dp));//十年OI一场空 
        for(int i=1;i<=m;i++)a[i]=read();
        for(int i=1;i<=m;i++){
            for(int j=m;j>=i;j--){
                dp[i][j]=max(dp[i-1][j]+pw[m-j+i-1]*a[i-1],dp[i][j+1]+pw[m-j+i-1]*a[j+1]);
            } 
        } 
        __int128 maxh=0;
        for(int i=1;i<=m;i++)maxh=max(maxh,dp[i][i]+pw[m]*a[i]);//找出每行的最优解 
        ans+=maxh;
    }
    print(ans);
    return 0;//十年OI两场空 
}//完结撒花,感谢陪伴 

 

posted @ 2024-03-02 20:20  iridescent94  阅读(33)  评论(0)    收藏  举报  来源