wikioi p1166 矩阵取数游戏
2013-08-25 13:13
274 查看
这题是动规还好 问题是要加功能很齐全的高精度 一下子就变得恶心了
高精度没法说.....就不说了...
dp[i][j] 表示计算的那一层还剩下为 [ i , j ] 的区间 保存之前取数的最大值
首先因为每一层求得最值都互不影响
所以for( res 1->n)枚举每一层的最大值
然后for( k 1->m)枚举第几次取数 同时计算2的k次幂
最后枚举左区间( L 1->k) 因为第k次取数该排最多只能取k个 所以L枚举到k就行了
通过k和L可以计算出右区间端点 R = m + L - 1 - k ;
动规方程为dp[L][R] = max(dp[L - 1][R] + (2^k )* map[res][L - 1], dp[L][R + 1] + (2^k) * map[res][R + 1])
每一层取完数字的最大值就是之前计算的dp[i][j]中的一个最大值 ans = max(dp[i][j] , ans)
再吧ans累加即可得到答案
一开始看错题目了,以为是总共取M次,谁知道是每次M次,那这题就简单了,没的说。
但是高精度有点烦。
现在终于自己写出来了
/*
Sour : NOIPSenior
ID : Thaddeus
PID : 1005
*/
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<memory.h>
using namespace std;
const int maxn = 81;
const int maxm = 5;
const long long radix = 1000000000;
long long a[maxn][maxn][maxm];
long long f[maxn][maxn][maxn][maxm]; //第i行头取j~k的最大值
long long r[maxn][maxm]; //二进制数
long long ans[maxm];
long long mul1[maxm];
long long mul2[maxm];
long long com1[maxm];
long long com2[maxm];
long long zero[maxm];
long long two[maxm];
long long temp[maxm];
int n,m;
void init()
{
int i,j;
scanf("%d %d",&n,&m);
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
scanf("%lld",&a[i][j][1]);
}
bool comp(long long x[],long long y[]) //判断数字x是否比y大,如果是,返回true
{
int i;
for (i=maxm-1;i>=1;i--)
{
if (x[i]>y[i]) return true;
if (x[i]<y[i]) return false;
}
return true;
}
void plu(long long x[],long long y[],long long z[]) //将x与y相加的值记录到z中
{
int i;
for (i=0;i<maxm;i++)
z[i]=0;
for (i=1;i<maxm;i++)
{
z[i]+=x[i]+y[i];
z[i+1]+=z[i]/radix;
z[i]%=radix;
}
for (i=1;i<maxm;i++)
z[i]+=z[i-1]/radix,z[i-1]%=radix;
}
void mult(long long x[],long long y[],long long z[])//将x与y相乘的值记录到z中
{
int i,j;
for (i=0;i<maxm;i++)
z[i]=0;
for (i=1;i<maxm;i++)
for (j=1;j<maxm;j++)
if (i+j<=maxm)
{
z[i+j-1]+=x[i]*y[j];
z[i+j]+=z[i+j-1]/radix;
z[i+j-1]%=radix;
}
for (i=0;i<maxm;i++)
{
z[i+1]+=z[i]/radix;
z[i]%=radix;
}
}
void _put(long long x[])
{
int i;
i = maxm-1;
while(!x[i]&&i>1) --i;
printf("%lld",x[i--]);
for (;i>=1;i--)
printf("%09lld",x[i]);
}
void _init()
{
int i;
memset(ans,0,sizeof(ans));
memset(f,0,sizeof(f));
memset(zero,0,sizeof(zero));
memset(two,0,sizeof(two));
memset(r,0,sizeof(r));
two[1]=2;
r[0][1]=1;
for (i=1;i<=m;i++)
mult(r[i-1],two,r[i]);
}
void work()
{
int i,j,k,p;
for (i=1;i<=n;i++)
{
for (j=1;j<=m;j++)
mult(a[i][j],r[m],f[i][j][j]);
for (p=1;p<=m;p++)
for (j=1;j+p<=m;j++)
{
k = p+j;
mult(a[i][j],r[m-p],mul1);
mult(a[i][k],r[m-p],mul2);
plu(mul1,f[i][j+1][k],com1);
plu(mul2,f[i][j][k-1],com2);
if (comp(com1,com2))
plu(com1,zero,f[i][j][k]);
else
plu(com2,zero,f[i][j][k]);
}
}
}
void put()
{
int i;
for (i=1;i<=n;i++)
plu(ans,f[i][1][m],temp),plu(temp,zero,ans);
_put(ans);
}
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
init();
_init();
work();
put();
return 0;
}
高精度没法说.....就不说了...
dp[i][j] 表示计算的那一层还剩下为 [ i , j ] 的区间 保存之前取数的最大值
首先因为每一层求得最值都互不影响
所以for( res 1->n)枚举每一层的最大值
然后for( k 1->m)枚举第几次取数 同时计算2的k次幂
最后枚举左区间( L 1->k) 因为第k次取数该排最多只能取k个 所以L枚举到k就行了
通过k和L可以计算出右区间端点 R = m + L - 1 - k ;
动规方程为dp[L][R] = max(dp[L - 1][R] + (2^k )* map[res][L - 1], dp[L][R + 1] + (2^k) * map[res][R + 1])
每一层取完数字的最大值就是之前计算的dp[i][j]中的一个最大值 ans = max(dp[i][j] , ans)
再吧ans累加即可得到答案
一开始看错题目了,以为是总共取M次,谁知道是每次M次,那这题就简单了,没的说。
但是高精度有点烦。
现在终于自己写出来了
/*
Sour : NOIPSenior
ID : Thaddeus
PID : 1005
*/
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<memory.h>
using namespace std;
const int maxn = 81;
const int maxm = 5;
const long long radix = 1000000000;
long long a[maxn][maxn][maxm];
long long f[maxn][maxn][maxn][maxm]; //第i行头取j~k的最大值
long long r[maxn][maxm]; //二进制数
long long ans[maxm];
long long mul1[maxm];
long long mul2[maxm];
long long com1[maxm];
long long com2[maxm];
long long zero[maxm];
long long two[maxm];
long long temp[maxm];
int n,m;
void init()
{
int i,j;
scanf("%d %d",&n,&m);
for (i=1;i<=n;i++)
for (j=1;j<=m;j++)
scanf("%lld",&a[i][j][1]);
}
bool comp(long long x[],long long y[]) //判断数字x是否比y大,如果是,返回true
{
int i;
for (i=maxm-1;i>=1;i--)
{
if (x[i]>y[i]) return true;
if (x[i]<y[i]) return false;
}
return true;
}
void plu(long long x[],long long y[],long long z[]) //将x与y相加的值记录到z中
{
int i;
for (i=0;i<maxm;i++)
z[i]=0;
for (i=1;i<maxm;i++)
{
z[i]+=x[i]+y[i];
z[i+1]+=z[i]/radix;
z[i]%=radix;
}
for (i=1;i<maxm;i++)
z[i]+=z[i-1]/radix,z[i-1]%=radix;
}
void mult(long long x[],long long y[],long long z[])//将x与y相乘的值记录到z中
{
int i,j;
for (i=0;i<maxm;i++)
z[i]=0;
for (i=1;i<maxm;i++)
for (j=1;j<maxm;j++)
if (i+j<=maxm)
{
z[i+j-1]+=x[i]*y[j];
z[i+j]+=z[i+j-1]/radix;
z[i+j-1]%=radix;
}
for (i=0;i<maxm;i++)
{
z[i+1]+=z[i]/radix;
z[i]%=radix;
}
}
void _put(long long x[])
{
int i;
i = maxm-1;
while(!x[i]&&i>1) --i;
printf("%lld",x[i--]);
for (;i>=1;i--)
printf("%09lld",x[i]);
}
void _init()
{
int i;
memset(ans,0,sizeof(ans));
memset(f,0,sizeof(f));
memset(zero,0,sizeof(zero));
memset(two,0,sizeof(two));
memset(r,0,sizeof(r));
two[1]=2;
r[0][1]=1;
for (i=1;i<=m;i++)
mult(r[i-1],two,r[i]);
}
void work()
{
int i,j,k,p;
for (i=1;i<=n;i++)
{
for (j=1;j<=m;j++)
mult(a[i][j],r[m],f[i][j][j]);
for (p=1;p<=m;p++)
for (j=1;j+p<=m;j++)
{
k = p+j;
mult(a[i][j],r[m-p],mul1);
mult(a[i][k],r[m-p],mul2);
plu(mul1,f[i][j+1][k],com1);
plu(mul2,f[i][j][k-1],com2);
if (comp(com1,com2))
plu(com1,zero,f[i][j][k]);
else
plu(com2,zero,f[i][j][k]);
}
}
}
void put()
{
int i;
for (i=1;i<=n;i++)
plu(ans,f[i][1][m],temp),plu(temp,zero,ans);
_put(ans);
}
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
init();
_init();
work();
put();
return 0;
}
相关文章推荐
- wikioi 1166 矩阵取数游戏
- wikioi 1166 矩阵取数游戏
- wikioi-天梯-普及一等-区间dp-1166:矩阵取数游戏
- wikioi 1166 矩阵取数游戏(2007年NOIP全国联赛提高组)
- wikioi 1166 矩阵取数游戏(2007年NOIP全国联赛提高组)
- 【wikioi】1166矩阵取数游戏
- wikioi 1166 矩阵取数游戏
- 第一人称游戏中的矩阵运用
- BZOJ 1059 矩阵游戏【二分图完美匹配】经典题
- [BZOJ1059][ZJOI2007][二分图匹配]矩阵游戏
- codevs1166 矩阵取数游戏
- [NOI2013]矩阵游戏
- [NOIp2007提高组]矩阵取数游戏
- BZOJ1059 / ZJOI2007 矩阵游戏【网络流/二分图】
- 洛谷1005 矩阵取数游戏(dp+高精)
- ZJOI2007矩阵游戏
- [BZOJ 3240][NOI 2013]矩阵游戏(数学+乘法逆元)
- NOIP2007 矩阵取数游戏
- BZOJ 1059 [ZJOI2007]矩阵游戏
- 【NOI2013T4】矩阵游戏-矩阵优化+十进制快速幂