看了数据范围,需高精度,不过,比赛中,是不编的,试试int128,先算算该题的数据最大值。
推导过程中,忘记了等比数列求和公式,自个重新推导了一遍。
n=80,m=80,aij=1000
80*1000*(2^1+2^2+2^3+......+2^80)
=8*10^4*(2-2*2^80)/(1-2)
=2^81*8*10^4
=193428131138340667952988160000
=2*10^29
int128最大值
2^127-1=1.7014118346046923173168730371588e+38
完全够用,
那么,该题 高精度靠边站,
用int128
区间动规,在一维层面处理惯了,二维层面极不适应。还是要从样例入手,找出规律。
看了一眼思路,发现完全想不到,那么套用思路,整理样例吧:
- 求n行最大得分和,每一行取数又不会影响到其他行,那么只要确保每一行得分最大,管好自家孩子就行了。(这个在动规中叫最优子结构)
- 每次取数是在边缘取,那么每次取数完剩下来的元素一定是在一个完整的一个区间中,又是求最优解,区间DP应运而生。
- 我们用f{i,j}表示区间变为[i,j]时,获得的最大分数。
- 当区间变为[i,j]时,上一次取数的时候区间一定是[i-1,j]或[i,j+1]从这两个状态转移即可。
样例2
输入:
1 4
4 5 0 5
输出:
122
位置1 2 3 4
数值4 5 0 5
取了0次
dp[1][4]=0
取了1次
dp[1][3]=5*2^1=10
dp[2][4]=4*2^1=8
取了2次
dp[1][2]=dp[1][3]+0*2^2=10
dp[2][3]=max(dp[1][3]+4*2^2,dp[2][4]+5*2^2)=max(26,28)=28
dp[3][4]=dp[2][4]+5*2^2=28
取了3次
dp[1][1]=dp[1][2]+5*2^3=50
dp[2][2]=max(dp[1][2]+4*2^3,dp[2][3]+0*2^3)=max(42,28)=42
dp[3][3]=max(dp[2][3]+5*2^3,dp[3][4]+5*2^3)=max(68,68)=68
dp[4][4]=dp[3][4]+0*2^3=28
取了4次,因无法找到合适的状态,故沿用取了3次的状态
dp[1][1]=50+4*2^4=114
dp[2][2]=42+5*2^4=122
dp[3][3]=68+0*2^4=68
dp[4][4]=28+5*2^4=108
取最大值,故输出122
先编写unsigned long long范围内的代码
有了上述模拟的基础,随便一遍,3个样例尽然都通过了,先放着,等int128编号后,再提交
以下是unsigned long long范围内的代码
ybt
未通过
测试点 | 结果 | 内存 | 时间 |
测试点1 | 答案正确 | 620KB | 2MS |
测试点2 | 答案正确 | 612KB | 2MS |
测试点3 | 答案正确 | 620KB | 2MS |
测试点4 | 答案正确 | 644KB | 2MS |
测试点5 | 答案正确 | 644KB | 2MS |
测试点6 | 答案错误 | 664KB | 2MS |
测试点7 | 答案错误 | 664KB | 2MS |
测试点8 | 答案错误 | 704KB | 3MS |
测试点9 | 答案错误 | 716KB | 3MS |
测试点10 | 答案正确 | 616KB | 2MS |
LOJ
LUOGU
1.从外围到内区间动归ULL60分
#include <bits/stdc++.h>
#define ULL unsigned long long
#define maxn 85
using namespace std;
ULL dp[maxn][maxn],a[maxn][maxn],two[maxn],ans=0,mx;
int main(){
int n,m,i,j,lt,rt,row,cnt,k;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%llu",&a[i][j]);
two[0]=1;
for(i=1;i<=m;i++)two[i]=two[i-1]*2;
for(row=1;row<=n;row++){//一行一行处理
for(i=1;i<=m;i++)
for(j=i;j<=m;j++)
dp[i][j]=0;//初始化
for(cnt=0;cnt<m;cnt++){//取数个数为cnt
for(k=0;k<=cnt;k++){
lt=k+1;//左边取k个
rt=m-(cnt-k);//右边取cnt-k个
dp[lt][rt]=max(dp[lt-1][rt]+a[row][lt-1]*two[cnt],dp[lt][rt+1]+a[row][rt+1]*two[cnt]);
}
}
for(i=1;i<=m;i++)//取数个数为m时
dp[i][i]+=a[row][i]*two[m];
mx=0;
for(i=1;i<=m;i++)
mx=max(mx,dp[i][i]);
ans+=mx;
}
printf("%llu\n",ans);
return 0;
}
有了上面代码的基础,int128的代码很快编好,3个样例也都通过。
ybt
未通过
测试点 | 结果 | 内存 | 时间 |
测试点1 | 答案错误 | 596KB | 2MS |
测试点2 | 答案正确 | 612KB | 2MS |
测试点3 | 答案正确 | 648KB | 2MS |
测试点4 | 答案正确 | 676KB | 2MS |
测试点5 | 答案正确 | 684KB | 2MS |
测试点6 | 答案正确 | 696KB | 2MS |
测试点7 | 答案正确 | 736KB | 2MS |
测试点8 | 答案正确 | 796KB | 3MS |
测试点9 | 答案正确 | 804KB | 3MS |
测试点10 | 答案正确 | 620KB | 1MS |
LOJ
LUOGU
2.高精度替代者int128 90分
#include <bits/stdc++.h>
#define maxn 85
using namespace std;
__int128 dp[maxn][maxn],a[maxn][maxn],two[maxn],ans=0,mx;
inline __int128 read(){//int128读取
__int128 x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){//读取非数字字符
if(ch=='-')f=-1;
ch=getchar();
}
while('0'<=ch&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
inline void print(__int128 x){
if(x<0){
putchar('-');
x=-x;
}
if(x){
print(x/10);//递归写法
putchar(x%10+'0');
}
}
int main(){
int n,m,i,j,lt,rt,row,cnt,k;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
a[i][j]=read();
two[0]=1;
for(i=1;i<=m;i++)two[i]=two[i-1]*2;
for(row=1;row<=n;row++){//一行一行处理
for(i=1;i<=m;i++)
for(j=i;j<=m;j++)
dp[i][j]=0;//初始化
for(cnt=0;cnt<m;cnt++){//取数个数为cnt
for(k=0;k<=cnt;k++){
lt=k+1;//左边取k个
rt=m-(cnt-k);//右边取cnt-k个
dp[lt][rt]=max(dp[lt-1][rt]+a[row][lt-1]*two[cnt],dp[lt][rt+1]+a[row][rt+1]*two[cnt]);
}
}
for(i=1;i<=m;i++)//取数个数为m时
dp[i][i]+=a[row][i]*two[m];
mx=0;
for(i=1;i<=m;i++)
mx=max(mx,dp[i][i]);
ans+=mx;
}
print(ans);
return 0;
}
才90分,还有什么没考虑到?
仔细对了60分代码,发现第一个点是正确的,那么,还是__int128的读写有问题,需改进。
上述90分代码争对如下输入数据,没有输出
1 1
0
明显,上述争对__int128的输出有问题,0是无法输出的,要补上这个漏洞。此处需特判。
ybt
通过
测试点 | 结果 | 内存 | 时间 |
测试点1 | 答案正确 | 612KB | 2MS |
测试点2 | 答案正确 | 612KB | 1MS |
测试点3 | 答案正确 | 648KB | 1MS |
测试点4 | 答案正确 | 688KB | 2MS |
测试点5 | 答案正确 | 676KB | 2MS |
测试点6 | 答案正确 | 708KB | 2MS |
测试点7 | 答案正确 | 728KB | 2MS |
测试点8 | 答案正确 | 804KB | 3MS |
测试点9 | 答案正确 | 836KB | 3MS |
测试点10 | 答案正确 | 616KB | 1MS |
LOJ
LUOGU
3.补上int128输出0漏洞100分
#include <bits/stdc++.h>
#define maxn 85
using namespace std;
__int128 dp[maxn][maxn],a[maxn][maxn],two[maxn],ans=0,mx;
inline __int128 read(){//int128读取
__int128 x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){//读取非数字字符
if(ch=='-')f=-1;
ch=getchar();
}
while('0'<=ch&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
inline void print(__int128 x){
if(x<0){
putchar('-');
x=-x;
}
if(x){
print(x/10);//递归写法
putchar(x%10+'0');
}
}
int main(){
int n,m,i,j,lt,rt,row,cnt,k;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
a[i][j]=read();
two[0]=1;
for(i=1;i<=m;i++)two[i]=two[i-1]*2;
for(row=1;row<=n;row++){//一行一行处理
for(i=1;i<=m;i++)
for(j=i;j<=m;j++)
dp[i][j]=0;//初始化
for(cnt=0;cnt<m;cnt++){//取数个数为cnt
for(k=0;k<=cnt;k++){
lt=k+1;//左边取k个
rt=m-(cnt-k);//右边取cnt-k个
dp[lt][rt]=max(dp[lt-1][rt]+a[row][lt-1]*two[cnt],dp[lt][rt+1]+a[row][rt+1]*two[cnt]);
}
}
for(i=1;i<=m;i++)//取数个数为m时
dp[i][i]+=a[row][i]*two[m];
mx=0;
for(i=1;i<=m;i++)
mx=max(mx,dp[i][i]);
ans+=mx;
}
if(ans==0)putchar('0');//__int128的print函数的漏洞
else print(ans);
return 0;
}
该题习得什么?
学习了从外围到内的区间动归。
进一步强化了unsigned long long的读取%llu
int128的读写更加的熟练。
补上了int128输出是0的漏洞。