1574 矩阵取数游戏(NOIP2007 LOJ10152 LUOGU1005 提高+/省选-) 从外围到内区间动归ULL60分 高精度替代者int128 90分 补上int128输出0漏洞100分

总目录

在线测评地址(ybt)

在线测评地址(LOJ)

在线测评地址(LUOGU)

看了数据范围,需高精度,不过,比赛中,是不编的,试试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答案正确620KB2MS
测试点2答案正确612KB2MS
测试点3答案正确620KB2MS
测试点4答案正确644KB2MS
测试点5答案正确644KB2MS
测试点6答案错误664KB2MS
测试点7答案错误664KB2MS
测试点8答案错误704KB3MS
测试点9答案错误716KB3MS
测试点10答案正确616KB2MS

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答案错误596KB2MS
测试点2答案正确612KB2MS
测试点3答案正确648KB2MS
测试点4答案正确676KB2MS
测试点5答案正确684KB2MS
测试点6答案正确696KB2MS
测试点7答案正确736KB2MS
测试点8答案正确796KB3MS
测试点9答案正确804KB3MS
测试点10答案正确620KB1MS

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答案正确612KB2MS
测试点2答案正确612KB1MS
测试点3答案正确648KB1MS
测试点4答案正确688KB2MS
测试点5答案正确676KB2MS
测试点6答案正确708KB2MS
测试点7答案正确728KB2MS
测试点8答案正确804KB3MS
测试点9答案正确836KB3MS
测试点10答案正确616KB1MS

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的漏洞。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值