【NOIP2007提高组T3】矩阵取数游戏-动态规划+高精度

(本人本题完成于2016-7-20)

题目大意:对一个n行m列(1≤n,m≤80)的矩阵进行操作,每次在每一行的行首或行尾取出一个数,第i次取数所得的得分为:第1行所取的数*2^i+第2行所取的数*2^i+...+第n行所取的数*2^i,给定一个矩阵,问最后能得到的最大得分。

做法:由题目可以分析得知每一行是相对独立的,我们可以依次求出每一行可以得到的最大得分,再进行累加,就可以得到整个矩阵能得到的最大得分。设f[i][j]为从行首取i个,从行尾取j个的情况下能得到的最大得分,不难得出状态转移方程:f[i][j]=max(f[i-1][j]+a[h][i],f[i][j-1]+a[h][m-j+1]),其中h为当前正在处理的行数,a[1...n][1...m]存储的是矩阵内的元素。我们以取数次数k来划分阶段,每一阶段中求出f[0][k],...,f[i][k-i],...,f[k][0]。注意,操作过程中的数字可能很大,所以要用高精度数来表示。

以下是本人代码(写得比较烂,将就着看看吧......):

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,a[90][90]={0};
int f[90][90][51]={0};
int cmp1[51]={0},cmp2[51]={0},cmp3[51]={0},ans[51]={0},pwr[51]={0};
int s1[51]={0},s2[51]={0};

int main()
{
  scanf("%d %d",&n,&m);
  for(int i=1;i<=n;i++)
    for(int j=1;j<=m;j++)
	  scanf("%d",&a[i][j]);
  
  for(int h=1;h<=n;h++) //h:当前要处理的行
  {
    for(int i=1;i<=m;i++)
	  for(int j=1;j<=m;j++)
	    for(int k=1;k<=50;k++)
	      f[i][j][k]=0;
	for(int i=1;i<=50;i++) pwr[i]=0;
	pwr[1]=1; //pwr:存储2的次幂
	for(int k=1;k<=m;k++) //k:表示当前在进行第k次取数
	{
	  for(int i=1;i<=50;i++)
        pwr[i]*=2;
      for(int i=1;i<=50;i++)
        if (pwr[i]>=10) {pwr[i+1]+=pwr[i]/10;pwr[i]%=10;}
	  for(int i=0;i<=k;i++)
	  {
	    for(int j=1;j<=50;j++) s1[j]=0;
		for(int j=1;j<=50;j++) s2[j]=0;
		for(int j=1;j<=50;j++) cmp1[j]=0;
		for(int j=1;j<=50;j++) cmp2[j]=0;
	    for(int j=1;j<=50;j++)
		  s1[j]=pwr[j]*a[h][i];
		for(int j=1;j<=50;j++)
		  if (s1[j]>=10) {s1[j+1]+=s1[j]/10;s1[j]%=10;} //s1=pwr*a[h][i]
		for(int j=1;j<=50;j++)
		  s2[j]=pwr[j]*a[h][m-(k-i)+1];
		for(int j=1;j<=50;j++)
		  if (s2[j]>=10) {s2[j+1]+=s2[j]/10;s2[j]%=10;} //s2=pwr*a[h][m-(k-i)+1]
		if (i>0) //特殊判断,防止访问无效内存
		{
		  for(int j=1;j<=50;j++)
		  {
		    cmp1[j]+=f[i-1][k-i][j]+s1[j];
		    if (cmp1[j]>=10) {cmp1[j+1]+=cmp1[j]/10;cmp1[j]%=10;}
		  } //cmp1=f[i-1][k-i]+a[h][i]*pwr=f[i-1][k-i]+s1
		}
		if (k-i>0) //特殊判断,防止访问无效内存
		{
		  for(int j=1;j<=50;j++)
		  {
		    cmp2[j]+=f[i][k-i-1][j]+s2[j];
		    if (cmp2[j]>=10) {cmp2[j+1]+=cmp2[j]/10;cmp2[j]%=10;}
		  } //cmp2=f[i][k-i-1]+a[h][m-(k-i)+1]*pwr=f[i][k-i-1]+s2
		}
		bool flag=0;
		for(int j=50;j>=1;j--)
        {
          if (cmp1[j]>cmp2[j]) {flag=1;break;}
		  if (cmp1[j]<cmp2[j]) {flag=0;break;}
        } //比较两个高精度数的大小
		if (flag)
		{
		  for(int j=1;j<=50;j++)
		    f[i][k-i][j]=cmp1[j];
		}
		else
		{
		  for(int j=1;j<=50;j++)
		    f[i][k-i][j]=cmp2[j];
		}
	  }
	}
	for(int i=1;i<=50;i++) cmp3[i]=0;
	for(int i=0;i<=m;i++)
	{
	  bool flag=0;
	  for(int j=50;j>=1;j--)
	  {
		if (f[i][m-i][j]>cmp3[j]) {flag=1;break;}
		if (f[i][m-i][j]<cmp3[j]) {flag=0;break;}
	  }
	  if (flag)
	  {
		for(int j=1;j<=50;j++)
		cmp3[j]=f[i][m-i][j];
	  }
	} //求出该行最大得分
	for(int i=1;i<=50;i++)
	  ans[i]+=cmp3[i];
	for(int i=1;i<=50;i++)
	  if (ans[i]>=10) {ans[i+1]+=ans[i]/10;ans[i]%=10;} //将该行最大得分累加进最终解
  }
  
  bool flag=0;
  for(int i=50;i>=1;i--)
  {
    if (!flag&&ans[i]>0) flag=1;
	if (flag) printf("%d",ans[i]);
  }
  if (!flag) printf("0"); //结果可能为0,特殊判断
  
  return 0;
}

posted @ 2016-07-20 19:47  Maxwei_wzj  阅读(187)  评论(0编辑  收藏  举报