【2007年提高组3】矩阵取数游戏
Time Limit:10000MS Memory Limit:65536K
Total Submit:10 Accepted:3
Case Time Limit:1000MS
Description
问题描述
帅帅经常跟同学玩一个矩阵取数游戏,对于一个给定的n*m的矩阵,矩阵中每个元素Aij为非负整数。游戏规则如下:
1.每次取数时须从每行各去走一个元素,共N个.M次后取完矩阵所有元素;
2.每次取数都是一个得分值,为每行取数的得分之和,每行取数的得分=被取走的元素值*2(2
右上角有个i),其中i表示第i次取数(从1开始编号)
3.每次取走的各个元素只能是该原素所在行的行首或行尾.
4.游戏结束总得分为M次取数得分之和
Input
输入文件game.in包括N+1行
第1行为两个用空格隔开的整数N和M
第2-N+1行为N*M矩阵,其中每行有M个用单个空格隔开的非负整数
Output
输出文件game.out仅包含1行,为一个整数,即输入矩阵取数后的最大得分.
Sample Input
2 3
1 2 3
3 4 2
Sample Output
82
Hint
输入输出样例2
Game.in
1 4
4 5 0 5
Game.out
122
输入输出样例3
Game.in
2 10
96 56 54 46 86 12 23 88 80 43
16 95 18 29 30 53 88 83 64 67
Game.out
316994
限制
60%的数据满足:1<=n,m<=30,答案不超过10^16
100%的数据满足:1<=n,m<=80,0<=Aij<=1000
Source
稍微分析这题时可以发现每个n行是独立的,也就是说只要求出一行的最大得分,其他行的求法是一样的。很容易看出是要用动态规划,一开始我是这样想的,逆向从最后一次的状态逐渐向第一次转移。最后一次的状态很容易写出
(就是dp[1][i]=a[x][i] x代表要求的行),
倒数第二次就是在最后一次的基础上向两边扩充,但是发现在m=80的时候 空间可能会达到2^40次方。。这肯定不行的。
其实这题就是类似石子归并类的题目,但是到自己做的时候还是无法做出,看了别人的题解又勉强“才会”,不知道以后遇到类是的还会不会。。。
这题的动态转移方程其实就dp[L][R]=2*max(dp[L+1][R]+a[x][L],dp[L][R-1]+a[x][R]),其实就是在L-R之间最大得分。每次可以取最左边和最右边两种情况,其实感觉就是分治。在我想的思路上加上分治。
由于结果很大。还要用到高精度。所以我用JAVA大整数做的(偷懒点)。
下面是AC代码:
import java.util.*;
import java.math.*;
public class Main {
/**
* @param args
*/
static int [][] a = new int [100][100];
static BigInteger [][] dp = new BigInteger [100][100];
static BigInteger max_val(BigInteger a,BigInteger b)
{
if(a.max(b)==a) return a;
else return b;
}
public static BigInteger solve(int i,int l,int r){
if(l==r) dp[l][r]=BigInteger.valueOf(a[i][l]).multiply(BigInteger.valueOf(2));
if(dp[l][r].compareTo(BigInteger.ZERO)>=0)
return dp[l][r];
if(l==r)
return dp[l][r];
if(l+1<=r)
dp[l][r]=(solve(i,l+1,r).add(BigInteger.valueOf(a[i][l]))).multiply(BigInteger.valueOf(2));
if(l<=r-1)
dp[l][r]=max_val(dp[l][r],(solve(i,l,r-1).add(BigInteger.valueOf(a[i][r]))).multiply(BigInteger.valueOf(2)));
//System.out.println(dp[l][r]);
return dp[l][r];
}
public static void main(String[] args) {
// TODO Auto-generated method stub\
Scanner cin = new Scanner (System.in);
int n=cin.nextInt(),m=cin.nextInt();
BigInteger sum=BigInteger.ZERO;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[i][j]=cin.nextInt();
//for(i=1;i<=n;i++)
// for(j=1;j<=m;j++)
// System.out.println(a[i][j]);
for(int i=1;i<=n;i++){
for(int k=0;k<=99;k++)
for(int j=0;j<=99;j++)
dp[k][j]=BigInteger.valueOf(-1);
solve(i,1,m);
sum=sum.add(dp[1][m]);
}
System.out.println(sum);
}
}