zoukankan      html  css  js  c++  java
  • P1005 矩阵取数游戏[区间dp]

    题目描述

    帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的(m*n)的矩阵,矩阵中的每个元素(a_{i,j})均为非负整数。游戏规则如下:

    1. 每次取数时须从每行各取走一个元素,共n个。经过m次后取完矩阵内所有元素;
    2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
    3. 每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值( imes 2^i)*,其中i表示第i次取数(从1开始编号);
    4. 游戏结束总得分为m次取数得分之和。

    帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

    解析

    除了脑残高精度(反正窝用__int128硬生生水了过去,但是考场上不能用啊),是道还行的dp题。

    窝的做法比起其它题解的做法low了很多,时间和空间效率都不是十分优秀,而且也似乎有人用了,还比我快(哭。


    观察题目,容易发现我们只能对每行分开进行(dp),而对每行的(dp)实际上就是一个区间(dp),从大区间缩小到小区间。

    (dp[i][l][r][j])表示第(i)次取数时,第(j)行左边界取到(l),右边界取到(r)时的最优解。

    得到状态转移方程:

    [dp[i][l][r][j]=maxlimits_{i in [1,m],j in [1,n]} {dp[i-1][l-1][r][j]+num[j][l-1]*2^i,dp[i-1][l][r+1][j]+num[j][r+1]*2^i} ]

    如果直接这么写会炸空间。

    观察状态转移方程发现一个状态只与它上一个状态有关,于是考虑一个滚动数组优化。

    参考代码

    #include<cstdio>
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<ctime>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    #include<set>
    #include<map>
    #define N 101
    #define ll __int128
    #define INF 0x7ffffffff
    using namespace std;
    ll dp[2][N][N][N],n,m,a[N][N];//dp[i][l][r][j]表示第i次取数第j行的最大得分,左端点l,右端点r
    inline ll read()
    {
    	int f=1,x=0;char c=getchar();
    	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    inline ll qp(ll a,ll b)//快速幂
    {
    	ll ans=1;
    	for(;b;b>>=1){if(b&1)ans*=a;a*=a;}
    	return ans;
    }
    void print(ll x)//暴躁老哥,在线__int128
    {
        if(!x) return;
        if(x) print(x/10);
        putchar(x%10+'0');
    }
    int main()
    {
    	n=read(),m=read();
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j) a[i][j]=read();
    	memset(dp,~0x3f,sizeof(dp));
    	int now=0;
    	for(int i=1;i<=n;++i) dp[0][1][m][i]=0;//初始化,不细讲
    	for(int k=1;k<=m;++k){
    		now^=1;
    		for(int i=1;i<=n;++i){
    			for(int l=1;l<=k+1;++l){
    				ll r=l+m-k-1;
    				dp[now][l][r][i]=max(dp[now][l][r][i],max(dp[now^1][l-1][r][i]+a[i][l-1]*qp(2,k),dp[now^1][l][r+1][i]+a[i][r+1]*qp(2,k)));
    			}
    		}
    	}
    	ll ans=0;
    	for(int i=1;i<=n;++i){
    		ll maxx=-INF;
    		for(int l=1;l<=m;++l)、
                //寻找每一行的最优解
    			maxx=max(maxx,max(dp[now][l][l+1][i],dp[now][l+1][l][i]));
            	//最后一步会出现两种状态,都要统计
    		ans+=maxx;
    	}
    	if(!ans) printf("0
    ");
    	else print(ans);
    	return 0; 
    }
    
  • 相关阅读:
    svn_linux + apache 实现网页访问svn
    SVN_2008R2 搭建流程与规范
    mysql 简称
    论运维之故障排查思路与方法
    mac pro 基本使用
    防火墙之netfailt、iptables详解
    翻转单词顺序列(剑指offer)
    中缀变为后缀
    左旋转字符串(剑指offer)
    和为S的两个数字(剑指offer)
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/11348816.html
Copyright © 2011-2022 走看看