解题报告: CF2B

题目链接:CF2B The least round way
显然需要 (dp) 解决。
第一个思路:用 (dp_{i}{j})代表第 (i) 行第 (j) 列最小的后缀 (0) 数,不幸的是,他被 hack 了。
考虑 (10)的形成,贡献只来自于 (2)(5) 这个因数。
我们容易想到最小化 (min{sum a_i,sum b_i})
ps:(sum a_i) 代表路径中数含有因子 (2) 的数量,(sum b_i) 代表路径中数含有因子 (5) 的数量。
于是我们就有了两个子任务,最小化 (sum a_i)(sum b_i),然后再取 (min)
这就是两个简单 (dp) 了(我没骗你吧)。
我们记 (dp1_{i,j}) 为到坐标为 ((i,j)) 的点最小的 (sum a_i)
我们记 (dp2_{i,j}) 为到坐标为 ((i,j)) 的点最小的 (sum b_i)
可以得到递推式:

[dp1_{i,j}=min{dp1_{i-1,j},dp1_{i,j-1}}+a_{i,j} ]

[dp2_{i,j}=min{dp2_{i-1,j},dp2_{i,j-1}}+b_{i,j} ]

对于 (a_{i,j})(b_{i,j}) 是可以直接预处理出来的(每次判断是否整除),这个处理的期望复杂度应该是 (mathcal O(n^2))
于是我们就能得到一个复杂度为 (mathcal O(n^2)) 的算法。
但是还有个坑:矩阵中的数可能为零,这是 (2)(5) 也没有贡献。
我们只需判断有没有答案为 (0) 的走法(乘积为 (0) 时答案为 (1)),如果不存在,只需找一条经过 (0) 的路线即可。
废话不多说,上代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;

#define read(x) scanf("%d",&x)
#define readl(x) scanf("%lld",&x)
#define ll long long 
#define inf 2147483647

int n;
int x[1005][1005];
int a[1005][1005],b[1005][1005],dp1[1005][1005],dp2[1005][1005];
int sum1[1005][1005],sum2[1005][1005];
int op1[1005][1005],op2[1005][1005];
int ans[1000005],cnt=0;
int flag=0,xx,yy; 

inline int cal(ll n,int a)
{
	int ans=0;
	while(n%a==0&&n!=0) ans++,n/=a;
	return ans;
}

int main()
{
	read(n);
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) readl(x[i][j]),dp1[i][j]=inf,dp2[i][j]=inf;
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(x[i][j]==0) flag=1,xx=i,yy=j;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++) a[i][j]=cal(x[i][j],2),b[i][j]=cal(x[i][j],5);
	}
	dp1[1][1]=a[1][1],sum1[1][1]=b[1][1];
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(i==n&&j==n) continue;
			if(i<n)
			{
				if(dp1[i+1][j]>=dp1[i][j]+a[i+1][j]) 
				{
					dp1[i+1][j]=dp1[i][j]+a[i+1][j];
					op1[i+1][j]=1;
					sum1[i+1][j]=sum1[i][j]+b[i+1][j];
				}
			}
			if(j<n)
			{
				if(dp1[i][j+1]>=dp1[i][j]+a[i][j+1])
				{
					dp1[i][j+1]=dp1[i][j]+a[i][j+1];
					op1[i][j+1]=2;
					sum1[i][j+1]=sum1[i][j]+b[i][j+1];
				}
			}
		}
	}
	dp2[1][1]=b[1][1],sum2[1][1]=a[1][1];
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			if(i==n&&j==n) continue;
			if(i<n)
			{
				if(dp2[i+1][j]>=dp2[i][j]+b[i+1][j]) 
				{
					dp2[i+1][j]=dp2[i][j]+b[i+1][j];
					op2[i+1][j]=1;
					sum2[i+1][j]=sum2[i][j]+a[i+1][j];
				}
			}
			if(j<n)
			{
				if(dp2[i][j+1]>=dp2[i][j]+b[i][j+1]) 
				{
					dp2[i][j+1]=dp2[i][j]+b[i][j+1];
					op2[i][j+1]=2;
					sum2[i][j+1]=sum2[i][j]+b[i][j+1];
				}
			}
		}
	}
	if(min(dp1[n][n],dp2[n][n])>=1&&flag)
	{
		printf("1
");
		for(int i=2;i<=yy;i++) putchar('R');
		for(int i=2;i<=n;i++) putchar('D');
		for(int i=yy+1;i<=n;i++) putchar('R');
	}
	else if(dp1[n][n]<=dp2[n][n])
	{
		printf("%d
",min(dp1[n][n],sum1[n][n]));
		int x=n,y=n;
		while(x+y!=2)
		{
			if(op1[x][y]==1) ans[++cnt]=1,x-=1;
			else ans[++cnt]=2,y-=1;
		}
		for(int i=cnt;i>=1;i--) 
		{
			if(ans[i]==1) printf("D");
			else printf("R");
		}
	}
	else 
	{
		printf("%d
",min(dp2[n][n],sum2[n][n]));
		int x=n,y=n;
		while(x+y!=2)
		{
			if(op2[x][y]==1) ans[++cnt]=1,x-=1;
			else ans[++cnt]=2,y-=1;
		}
		for(int i=cnt;i>=1;i--) 
		{
			if(ans[i]==1) printf("D");
			else printf("R");
		}
	}
	putchar('
');
	return 0;
}
原文地址:https://www.cnblogs.com/tlx-blog/p/12706147.html