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\) 的个数:
因子 \(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;
}