【2007年提高组3】矩阵取数游戏

【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);

	}

}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值