Codeforces 2B The least round way

首先我们都知道 10 = 5 × 2 10=5\times 2 10=5×2。所以我们只需要去用 DP 来解决此题。

先来讲一个典型的错误。可能就我犯过这个错误/kk

我们如果定义 f i , j f_{i,j} fi,j 为从 ( 1 , 1 ) (1,1) (1,1) ( i , j ) (i,j) (i,j) 的路上因子 2 2 2 和因子 5 5 5 的个数的最小值的最小值,那么我们就走上了一条不归路像我一样。我们看下面这组数据:

2
1 125
10 8

如果按照我们上面的定义,那么我们的路就选择了 ( 1 , 1 ) − > ( 1 , 2 ) − > ( 2 , 2 ) (1,1)->(1,2)->(2,2) (1,1)>(1,2)>(2,2),末尾有三个 0 0 0。而我们选择 ( 1 , 1 ) − > ( 2 , 1 ) − > ( 2 , 2 ) (1,1)->(2,1)->(2,2) (1,1)>(2,1)>(2,2),末尾有只有两个 0 0 0。所以答案错误。

正解

我们换一个定义方式:我们定义 f i , j f_{i,j} fi,j 为从 ( 1 , 1 ) (1,1) (1,1) ( i , j ) (i,j) (i,j) 的路上因子 2 2 2 的个数的最小值。我们可以得到转移方程, calc ⁡ ( x ) \operatorname{calc}(x) calc(x) 表示 x x x 中所含因子 2 2 2 的个数:

{ f i , j = f i − 1 , j + calc ⁡ ( x ) f i − 1 , j ≤ f i , j − 1 f i , j = f i , j − 1 + calc ⁡ ( x ) f i − 1 , j > f i , j − 1 \begin{cases} f_{i,j}=f_{i-1,j}+\operatorname{calc}(x)&f_{i-1,j\leq f_{i,j-1}}\\ f_{i,j}=f_{i,j-1}+\operatorname{calc}(x)&f_{i-1,j> f_{i,j-1}} \end{cases} {fi,j=fi1,j+calc(x)fi,j=fi,j1+calc(x)fi1,jfi,j1fi1,j>fi,j1

因子 5 5 5 计算方式相同,这里不再赘述。

最后的答案是 f n , n f_{n,n} fn,n 计算出的因子 5 5 5 的个数和因子 2 2 2 的个数的最小值。因为我们完全可以让 5 5 5 去顺应 2 2 2。因为没有 2 2 2,再多 5 5 5 也没有用处。如果同时然 2 , 5 2,5 2,5 最小,也就是上面的错误示例,一定是得不到正确答案的。

路径的处理就可以在 DP 是就处理好,最后倒退一遍即可。

慢着,我们还有一个非常重要的事情没有讲,就是 0 0 0 的特判

如果路线上含有 0 0 0 那么答案一定为 1 1 1。比如这一组数据:

8 125 24
2025 12 12
0 56 425

我们按照上南面的方法计算,我们会得到答案 3 3 3。然而,我们因为有 0 0 0,所以答案一定为 0 0 0。我们需要特殊处理路径。注意边界

#include<bits/stdc++.h>
#define int long long
using namespace std;

int read() {
	char ch=getchar();
	int f=1,x=0;
	while(ch<'0'||ch>'9') {
		if(ch=='-')
			f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9') {
		x=x*10+ch-'0';
		ch=getchar();
	}
	return f*x;
}

const int maxn=1001; 

int n,a[maxn][maxn],zx,zy;
bool zero;
char ans[maxn<<1];

struct pr {
	int two,five;
	int dt,df;
}f[maxn][maxn];

pr calc(int x) {
	pr ret;
	ret.five=ret.two=0;
	
	if(x==0) {
		return ret;//0要特判
	}
	
	while(x%2==0) {
		ret.two++;
		x>>=1;
	}
	while(x%5==0) {
		ret.five++;
		x/=5;
	}
	return ret;
}

signed main() {
	
	n=read();
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=n;j++) {
			a[i][j]=read();
			if(!a[i][j]) {
				zero=1;
				zx=i;
				zy=j;
			}
		}
	}
	
	for(int i=2;i<=n;i++) {
		f[0][i].five=f[0][i].two=f[i][0].five=f[i][0].two=0x7fffffff;
	}
	
	for(int i=1;i<=n;i++) {
		for(int j=1;j<=n;j++) {
			pr tmp=calc(a[i][j]);
			
			if(f[i-1][j].two<f[i][j-1].two) {
				f[i][j].two=f[i-1][j].two+tmp.two;
				f[i][j].dt='D';//可以同时维护路径
			}
			else {
				f[i][j].two=f[i][j-1].two+tmp.two;
				f[i][j].dt='R';
			}
			if(f[i-1][j].five<f[i][j-1].five) {
				f[i][j].five=f[i-1][j].five+tmp.five;
				f[i][j].df='D';
			}
			else {
				f[i][j].five=f[i][j-1].five+tmp.five;
				f[i][j].df='R';
			}
		}
	}
	
	if(zero&&min(f[n][n].five,f[n][n].two)>1) {//0要特判
		cout<<1<<endl;
		for(int i=1;i<zy;i++) {
			cout<<'R';
		}
		for(int i=1;i<n;i++) {//注意边界哦~
			cout<<'D';
		}
		for(int i=zy+1;i<=n;i++) {
			cout<<'R';
		}
		return 0;
	}
	
	int t;
	if(f[n][n].five<f[n][n].two) {
		t=1;
		cout<<f[n][n].five<<endl;
	}
	else {
		t=2;
		cout<<f[n][n].two<<endl;
	}
	
	int x=n,y=n,cnt=0;
	
	if(t==1) {
		while(!(x==1&&y==1)) {
			ans[++cnt]=f[x][y].df;
			if(f[x][y].df=='D') {
				x--;
			}
			else {
				y--;
			}
		}
	}
	else {
		while(!(x==1&&y==1)) {
			ans[++cnt]=f[x][y].dt;
			if(f[x][y].dt=='D') {
				x--;
			}
			else {
				y--;
			}
		}
	}
	
	for(int i=cnt;i;i--) {
		cout<<ans[i];
	}
	cout<<endl;
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Codeforces Round 887是一个程序设计竞赛活动,由Codeforces组织举办。根据引用中的代码,该竞赛的题目要求解决一个序列操作的问题。给定一个长度为n的序列,通过执行一系列操作,使得序列变得非sorted,即非严格递增。具体操作是将序列中[1, i]范围内的数字全部加一,同时将[i+1, n]范围内的数字全部减一。问题要求求解最少需要执行多少次操作才能达到要求。 引用中的代码给出了解决这个问题的实现。代码首先读入序列的长度n和序列a。然后通过判断序列是否已经是非sorted,如果是则直接输出0。接下来,代码遍历序列,求出相邻两个数字的差的最小值。最后,计算出最少需要执行的操作次数,并输出结果。 需要注意的是,引用中的代码只是给出了解决问题的一种实现方式,并不代表Codeforces Round 887的具体题目和解答。要了解该竞赛的具体信息,需要参考Codeforces官方网站或相关资料。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [Codeforces Round 887 (Div. 2)](https://blog.csdn.net/qq_36545889/article/details/131905067)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值