生平第一次独立做出和省选沾边的题,发个题解记录一下♪(^∀^●)ノ
和大部分题解一样,用到了dp思想:区间(l,r)可以由左面取得一个数字{(l-1.r)},或者是右面取一个数字{(l,r+1)}获得,取两者之间的最大值即为最优解。
因此伪代码如下:
如果 l不是区间的左边界( 1 )
那么就可以由左面推过来
ans1=f[l-1][r]+num[l-1]*2(的倍数)
如果 r不是区间的右边界( m )
那么就可以由右面推过来
ans2=f[l][r+1]+num[r+1]*2(的倍数)
f[l,r]=max(ans1,ans2);
那么程序就可以出炉了。
但是,这么做只能得到60分,因为数据限制,我们需要用到高精度才能ac
我看到很多题解提到了int128这么一个玩意儿,但是发现我的编译器(dev)居然不认,而且网上查了查资料感觉这个玩意儿并不是善茬...
仔细观察题意,发现每一次的得分都必定是2的倍数。那么...就可以模拟一下二进制。
开了201个数组(防止re嘛,具体多少我也没算),利用类似于快速幂的思想,若当前要假如一个x*2^m,那么只需要从第m+1的位置(因为二进制里第一位是2^0)开始,将x打成二进制,每一次都x>>=1向右滚动,滚动了n位之后,当x&1==1了,那么就把数组的m+n+1位++。
如此一来,只需要在最后一次输出时,将得到的二进制数转化为一个高精度十进制数,输出答案即可。
这里,有几点我做题时被坑了,汇总一下:
-
采用这种处理方法可能会导致二进制数组的某一位置的值>=2,因此不能贪空间而开bool型数组,并且每一次操作后都要检查一遍,将>=2的数位向上进位
-
在最终输出答案的时候,要特判一下长度是否为零。否则可能因为答案就是0,但长度认为0而不输出任何结果。(我就因为这个原因得了90分,第一个点wa了)
-
二进制的第一位是2^0因此加一个数字的时候,例如x*2^m,一定要从m+1开始!
-
各种杂七杂八的进位处理问题,只能说处理的时候要小心.千万小心!当你惆怅了半天查不出bug,结果发现把+=写成++了,那感觉......
最后附上代码,第一次发题解,长度拿捏不好
( # ▽ # )
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
using namespace std;
int n,m,num[81][81],ans[201],ansp[201],f[81][81][201];
void work(int[]);
void adjust(int[]);
void intcpy(int[],int[]);
int intcmp(int[],int[]);
void intplus(int[],int[]);
void print(int[]);
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
scanf("%d",&num[i][j]);
}
}
for(int i=1;i<=n;++i){
work(num[i]);
intplus(ans,ansp);
adjust(ans);
}
print(ans);
return 0;
}
void work(int x[]){
memset(f,0,sizeof(f));
for(int i=1;i<=m;++i){
for(int j=m;j>=i;--j){
//怀疑是不是cnt的鬼m-j+i;
int cnt=m-j+i,ans1[201]={0},ans2[201]={0};
//if(i>1) f[i][j]=max(f[i][j],f[i-1][j]+x[i-1]*cnt);
if(i>1){
intcpy(ans1,f[i-1][j]);
int tmpcnt=cnt,plust=x[i-1];
while(plust){
if(plust&1){
ans1[tmpcnt]++;
}
tmpcnt++;plust>>=1;
}
adjust(ans1);
}
//if(j<m) f[i][j]=max(f[i][j],f[i][j+1]+x[j+1]*cnt);
if(j<m){
intcpy(ans2,f[i][j+1]);
int tmpcnt=cnt,plust=x[j+1];
while(plust){
if(plust&1){
ans2[tmpcnt]++;
}
tmpcnt++;plust>>=1;
}
adjust(ans2);
}
if(intcmp(ans1,ans2)>=1){
intcpy(f[i][j],ans1);
}
else{
intcpy(f[i][j],ans2);
}
}
}
for(int i=1;i<=m;++i){
//f[i][i]+=(x[i]*cnt);
int plust=x[i],cnt=m+1;
while(plust){
if(plust&1){
f[i][i][cnt]+=1;
}
cnt++;plust>>=1;
}
adjust(f[i][i]);
}
int tmp[201]={0};
intcpy(tmp,f[1][1]);
for(int i=2;i<=m;++i){
if(intcmp(tmp,f[i][i])==-1){
intcpy(tmp,f[i][i]);
}
}
//wtf?
/*for(int i=tmp[0];i>=1;--i){
tmp[i+1]=tmp[i];
}
tmp[1]=0;tmp[0]=tmp[0]+1;*/
//
memset(ansp,0,sizeof(ansp));
intcpy(ansp,tmp);
}
void adjust(int a[]){
for(int i=1;i<=201;++i){
/*int cnt=i,tmp=a[i];a[i]=0;
while(tmp){
if(tmp&1) a[cnt]++;
cnt++;tmp>>=1;
}*/
a[i+1]+=a[i]/2;
a[i]%=2;
}
int len=200;
while(a[len]==0&&len>1){
len--;
}
a[0]=len;
}
void intcpy(int a[],int b[]){
for(int i=0;i<=b[0];++i){
a[i]=b[i];
}
}
int intcmp(int a[],int b[]){
if(a[0]>b[0]) return 1;
if(a[0]<b[0]) return -1;
for(int i=a[0];i>=1;--i){
if(a[i]>b[i]) return 1;
if(a[i]<b[i]) return -1;
}
return 0;
}
void intplus(int a[],int b[]){
int len=1;
while(len<=a[0]||len<=b[0]){
a[len]+=b[len];
len++;
}
len=200;
while(a[len]==0&&len>1) len--;
a[0]=len;
}
void print(int x[]){
int ans[200]={0},plust[200]={0};
plust[0]=1;plust[1]=1;
for(int i=1;i<=x[0];++i){
if(x[i]==1){
//plus model:
int len=1;
while(len<=plust[0]||len<=ans[0]){
ans[len]+=plust[len];
ans[len+1]+=ans[len]/10;
ans[len]%=10;
len++;
}
if(ans[len]==0) len--;
ans[0]=len;
}
//time model:
int k=0;
for(int i=1;i<=plust[0];++i){
plust[i]*=2;
plust[i]+=k;
k=plust[i]/10;
plust[i]%=10;
}
if(k!=0) plust[++plust[0]]=k;
}
if(ans[0]==0){
printf("0");
}
else{
for(int i=ans[0];i>=1;--i){
printf("%d",ans[i]);
}
printf("\n");
}
}