1. 程式人生 > 其它 >【CF2B The least round way】題解

【CF2B The least round way】題解

題目連結

題目

There is a square matrix n × n, consisting of non-negative integer numbers. You should find such a way on it that

  • starts in the upper left cell of the matrix;
  • each following cell is to the right or down from the current cell;
  • the way ends in the bottom right cell.

Moreover, if we multiply together all the numbers along the way, the result should be the least "round". In other words, it should end in the least possible number of zeros.

給定由非負整陣列成的\(n \times n\) 的正方形矩陣,你需要尋找一條路徑:

以左上角為起點

每次只能向右或向下走

以右下角為終點
並且,如果我們把沿路遇到的數進行相乘,積應當是最小“round”,換句話說,應當以最小數目的0的結尾.

思路

要使0結尾最少,也就是要使以2結尾和以5結尾最少,因為0的個數並不是由多的那個因子覺定的,而是由少的那個因子覺定的,所以我們可以看一下從起點到終點2和5最少出現多少次,取個min值就是答案,這個過程可以用dp來實現。

要注意的是,如果出現0,那麼必然存在一條道路只有1個0結尾,這時我們就要與前面那種情況作比較,有更優的輸出。

總結

一開始做這道題時想的是要把其中一個因子(也就是2或5)的個數壓到dp轉態中(就是 \(dp(i,j,k)\)

表示在 \((i,j)\) 號格子當5的個數為 \(k\) 時,2的個數最少為 \(dp(i,j,k)\)),類似一種dp的方法,這使我想不出怎麼優化。

後來看題解,發現本質上0的個數是由2和5中較少那個數的數量覺定的,這個思路挺妙。其實對於這種題,尤其是揹包累,當要存的轉態多時,可以看看答案是否有小的那個數量覺定。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
//#define M
//#define mo
#define N 1010
int n, m, i, j, k; 
int a[N][N], dp[N][N][2], f[N][N][2], ans; 

void dfs(int x, int y, int k)
{
	// printf("%d %d %d\n", x, y, k); 
	// if(x==1||y==1) return ; 
	// if(x==1&&y==1) return ; 
	if(f[x][y][k]==1) dfs(x, y-1, k), putchar('R'); 
	if(f[x][y][k]==-1) dfs(x-1, y, k), putchar('D'); 
}

signed main()
{
//	freopen("tiaoshi.in", "r", stdin); 
//	freopen("tiaoshi.out", "w", stdout); 
	memset(dp, 0x3f, sizeof(dp)); 
	dp[0][1][0]=dp[0][1][1]=0; 
	ans=0x7fffffff; 
	n=read(); 
	for(i=1; i<=n; ++i)
		for(j=1; j<=n; ++j)	
		{
			a[i][j]=read(); 
			if(!a[i][j]) ans=1; 
			while(a[i][j]&&a[i][j]%2==0) a[i][j]/=2, ++m; 
			dp[i][j][0]=min(dp[i-1][j][0], dp[i][j-1][0])+m; m=0; 
			f[i][j][0]=(dp[i-1][j][0]<dp[i][j-1][0] ? -1 : 1); 
			while(a[i][j]&&a[i][j]%5==0) a[i][j]/=5, ++m; 
			dp[i][j][1]=min(dp[i-1][j][1], dp[i][j-1][1])+m; m=0; 
			f[i][j][1]=(dp[i-1][j][1]<dp[i][j-1][1] ? -1 : 1); 
			if(i==1&&j==1) f[i][j][0]=f[i][j][1]=0; 
			// printf("%lld %lld\n", dp[i][j][0], dp[i][j][1]); 
		}
	printf("%lld\n", min(ans, min(dp[n][n][0], dp[n][n][1]))); 
	if(ans<min(dp[n][n][0], dp[n][n][1]))
	{
		for(i=1; i<=n; ++i)
			for(j=1; j<=n; ++j)
				if(!a[i][j])
				{
					for(k=1; k<i; ++k) printf("D"); 
					for(k=1; k<j; ++k) printf("R"); 
					for(k=i; k<n; ++k) printf("D"); 
					for(k=j; k<n; ++k) printf("R"); 
					return 0; 
				}
	}
	if(dp[n][n][0]<dp[n][n][1]) dfs(n, n, 0); 
	else dfs(n, n, 1); 
	return 0; 
}