Codeforces 2B The least round way

听说博客食用更佳

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

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

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

2
1 125
10 8

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

正解

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

\[\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}\]

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

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

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

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

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

8 125 24
2025 12 12
0 56 425

我们按照上南面的方法计算,我们会得到答案 \(3\)。然而,我们因为有 \(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;
}
posted @ 2020-07-29 14:41  huayucaiji  阅读(139)  评论(0编辑  收藏  举报