矩阵取数游戏 NOIP 2007

2016-05-31 17:26:45

题目链接: NOIP 2007 矩阵取数游戏(Codevs)

题目大意:

  给定一个矩阵,每次在每一行的行首或者行尾取一个数乘上2^次数,求取完最多获得的分数

解法:

  动态规划

  DP[i][j]表示当前行i位置到j位置获得的最大分数

  转移方程:

    DP[i][j]=max(DP[i+1][j]+a[i]*2^x,DP[i][j-1]+a[i]*2^x);

需要注意的地方:

  1.M和N给的范围略坑啊,2^80只有悄悄不说话了,所以直接上了100000的压位,感觉比高精好使多了

  2.2的次方多次用到,所以预先处理出来待用

  1 //矩阵取数游戏 (NOIP2007)
  2 //动态规划 高精度
  3 #include<stdio.h>
  4 #include<algorithm>
  5 #include<string.h>
  6 using namespace std;
  7 const int maxn=85;
  8 const int maxm=85;
  9 const int maxl=30;
 10 int N,M;
 11 int a[maxn];
 12 int DP[maxn][maxm][maxl];
 13 int ys[maxn][maxl];
 14 int ans[maxl];
 15 int tmp[maxl];
 16 void Multi(int *x,int *y,int z)
 17 {
 18     memset(tmp,0,sizeof(tmp));
 19     tmp[0]=y[0];
 20     for(int i=1;i<=tmp[0];i++)
 21     {
 22         tmp[i]=y[i]*z;
 23     }
 24     for(int i=1;i<=tmp[0];i++)
 25     {
 26         tmp[i+1]+=tmp[i]/100000;
 27         tmp[i]%=100000;
 28     }
 29     while(tmp[tmp[0]+1])
 30     {
 31         tmp[0]++;
 32         tmp[tmp[0]+1]+=tmp[tmp[0]]/100000;
 33         tmp[tmp[0]]%=100000;
 34     }
 35     for(int i=0;i<30;i++)x[i]=tmp[i];
 36     return ;
 37 }
 38 void Add(int *x,int *y,int *z)
 39 {
 40     memset(tmp,0,sizeof(tmp));
 41     if(y[0]>z[0])tmp[0]=y[0];
 42     else tmp[0]=z[0];
 43     for(int i=1;i<=tmp[0];i++)
 44     {
 45         tmp[i]=y[i]+z[i];
 46     }
 47     for(int i=1;i<=tmp[0];i++)
 48     {
 49         tmp[i+1]+=tmp[i]/100000;
 50         tmp[i]%=100000;
 51     }
 52     if(tmp[tmp[0]+1])tmp[0]++;
 53     for(int i=0;i<30;i++)x[i]=tmp[i];
 54     return ;
 55 }
 56 bool Max(int *x,int *y)
 57 {
 58     if(x[0]>y[0])return 1;
 59     else if(y[0]>x[0])return 0;
 60     else 
 61     {
 62         for(int i=x[0];i>=1;i--)
 63         {
 64             if(x[i]>y[i])return 1;
 65             else if(x[i]<y[i])return 0;
 66         }
 67     }
 68     return 1;
 69 }
 70 int main()
 71 {
 72     scanf("%d %d",&N,&M);
 73     ys[0][0]=1;
 74     ys[0][1]=1;
 75     for(int i=1;i<=M;i++)
 76     {
 77         Multi(ys[i],ys[i-1],2);
 78     }
 79     ans[0]=1;ans[1]=0;
 80     for(int i=1;i<=N;i++)
 81     {
 82         for(int j=1;j<=M;j++)
 83         {
 84             scanf("%d",&a[j]);
 85             Multi(DP[j][j],ys[M],a[j]);
 86         }
 87         for(int k=2;k<=M;k++)
 88         {
 89             for(int x=1;x<=M-k+1;x++)
 90             {
 91                 int y=x+k-1;
 92                 int xm[31],ym[31],cm[31];
 93                 Multi(cm,ys[M-k+1],a[x]);
 94                 Add(xm,cm,DP[x+1][y]);
 95                 Multi(cm,ys[M-k+1],a[y]);
 96                 Add(ym,cm,DP[x][y-1]);
 97                 if(Max(xm,ym))
 98                 {
 99                     memcpy(DP[x][y],xm,sizeof(int)*30);
100                 }
101                 else 
102                 {
103                     memcpy(DP[x][y],ym,sizeof(int)*30);
104                 }
105             }
106         }
107         Add(ans,ans,DP[1][M]);
108     }
109     printf("%d",ans[ans[0]]);
110     for(int i=ans[0]-1;i>=1;i--)
111     {
112         printf("%05d",ans[i]);
113     }
114     printf("\n");
115     return 0;
116 }

 

转载于:https://www.cnblogs.com/Neptune-/p/5546711.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值