【提高组NOIP2007】矩阵取数游戏

题目描述
帅帅经常跟同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素a[i,j] 均为非负整数。游戏规则如下:

每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有元素;

每次取走的各个元素只能是该元素所在行的行首或行尾;

每次取数都有一个得分值,为每行取数的得分之和,每行取数的得分 = 被取走的元素值*2^i,其中i表示第i次取数(从1开始编号);

游戏结束总得分为m次取数得分之和。帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

输入
输入文件game.in包括n+1行:

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

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

输出
输出文件game.out仅包含1行,为一个整数,即输入矩阵取数后的最大得分。

样例输入
Sample Input1:

2 3

1 2 3

3 4 2

扫描二维码关注公众号,回复: 4717032 查看本文章

Sample Input2:

1 4

4 5 0 5

Sample Input3:

2 10

96 56 54 46 86 12 23 88 80 43

16 95 18 29 30 53 88 83 64 67

样例输出
Sample Output1:

82

【输入输出样例1解释】

第1次:第1行取行首元素,第2行取行尾元素,本次得分为1*21+2*21=6

第2次:两行均取行首元素,本次得分为2*22+3*22=20

第3次:得分为3*23+4*23=56。总得分为6+20+56=82

Sample Output2:

122

Sample Output3:

316994

数据范围限制
【限制】

60%的数据满足:1<=n, m<=30, 答案不超过10^16

100%的数据满足:1<=n, m<=80, 0<=a[i,j]<=1000

思路:
dp
可以发现每一行可以单独处理,答案就是每一行获得的最大分数的和。
在这里插入图片描述
要高精度,注意常数优化。

#include<bits/stdc++.h>
#pragma GCC optimize(3)
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
#define N 100
#define ll long long
using namespace std;


ll base=1000; 
ll work(ll b)
{
	ll ans=0;
	while(b)
		{
			ans++;
			b/=10;
		}
	return ans;
}
struct bignum
{
	ll len;
	ll num[100];
	bignum(){len=1;memset(num,0,sizeof num);}
	void init(){len=1;memset(num,0,sizeof num);}
	
	bool operator < (const bignum &y) const
	{
		bignum x=*this;
		if(x.len < y.len) return 1; 
		if(x.len > y.len) return 0;
		for(ll i=x.len;i>0;i--) if(x.num[i]>y.num[i]) return 0; else if(x.num[i]<y.num[i])return 1;
		return 0;
		
	}
	bignum operator =( ll b)
	{
		this->len=0;
		if(b==0)
		{
			this->len=1;
			return *this;
		}
		while(b)
		{
			this->num[++(this->len)]=b%base;
			b/=base;
		}
		return *this;
	}
	bignum operator +(const bignum b)
	{
		bignum &a=*this,c=bignum();
		ll tmp=max(a.len,b.len);
		for(ll i=1;i<=tmp;i++)c.num[i]=a.num[i]+b.num[i];
		for(ll i=1;i<=tmp;i++){c.num[i+1]+=c.num[i]/base;c.num[i]%=base;}
		if(c.num[tmp+1]>0)c.len=tmp+1;else c.len=tmp;
		return c;
		
	}
	bignum operator *(const ll a)
	{
		ll tmp=0;
		bignum b=*this;
		for(ll i=1;i<=b.len;i++)b.num[i]*=a;
		for(ll i=1;i<=b.len+3;i++)
			{b.num[i+1]+=b.num[i]/base;b.num[i]%=base;}
		if(b.num[b.len+3])b.len+=3;else if(b.num[b.len+2])b.len+=2;else if(b.num[b.len+1])b.len+=1;
		return b;
	}
	void print()
	{
		bignum a=*this;
		ll count=work(base)-1,temp=0;
		for(ll i=a.len;i>0;i--)
		{
			temp=work(a.num[i]);
			if(i!=a.len)while(temp++<count)printf("0");
			printf("%lld",a.num[i]);
		}
	}
}f[N][N],ans,t,g[100];

ll n,m,i,j,k;
ll a[N][N];

bignum make(ll x,ll p)
{
	bignum a;
	a=g[p]*x;
	return a;
}

void dp(ll a[])
{
	ll i,j;
	for(i=1;i<=m;i++)
	{
		for(j=0;j<=i;j++)
		{
			f[i][j].init();
			bignum tail=f[i-1][j]+make(a[m-i+j+1],i),head;
			if(j>0) head=f[i-1][j-1]+make(a[j],i);
			f[i][j]=max(head,tail);
		}
	}
}

int main()
{


	open("game");
	scanf("%lld%lld",&n,&m);	
	g[0].num[1]=1;
	for(i=1;i<=m;i++)g[i]=g[i-1]*2;
	for(i=1;i<=n;i++)for(j=1;j<=m;j++)scanf("%lld",&a[i][j]);
	for(i=1;i<=n;i++)
	{
		dp(a[i]);
		t.init();
		for(j=0;j<=m;j++)t=max(t,f[m][j]);
		ans=ans+t;
	}
	ans.print();
}

猜你喜欢

转载自blog.csdn.net/Com_man_der/article/details/85394006