codeforces2B 2000分dp

題目傳送門

題意:

n*n的矩陣,每個格子有一個非負整數。起點在左上角的格子,每次移動可以向下或向右。不能越界。終點是右下角的格子。

把路徑上的數乘起來,使乘積的後導0個數最少。0認爲有一個後導0。

數據範圍: \dpi{150}n \leqslant 1000 , 0 \leqslant a_i \leqslant 10^9 。 a_i 是矩陣上的數。

題解:

從起點到終點路徑上的數的乘積因子中2的個數設爲x,5的個數設爲y。如果不考慮0,答案就是min(x,y)。

如果路徑上有0,那麼後導0個數爲1,需特判。

這道題的2因子和5因子是獨立的。意識到這點就完了。

假設x的最小值是minx,y的最小值是miny。

設從起點到終點路徑上2的因子數是minx時,5的因子數是y。那麼(minx,y)是有關的二元組。

設從起點到終點路徑上5的因子數是miny時,2的因子數是x。那麼(x,miny)是有關的二元組。

不妨設minx<miny,那麼這個時候minx < miny <= y。容易知道路徑上後導0個數是minx。

然後dp並記錄路徑就好了。

感受:

這道題真的把我educate了。

這道題一直WA在變量名寫重了。前面那麼多測例沒WA是因爲在第31個測例纔用到寫重的部分。

代碼:

#include<bits/stdc++.h>
using namespace std ;
const int maxn = 1005 ;
const int inf = 0x3f3f3f3f ;
int n ;
int num[maxn][maxn][2] ;
int dp[maxn][maxn][2] , p[maxn][maxn][2] ;
void change(int a , int b , int c , int d , int id , int f)
{
	int x = dp[a][b][id] + num[c][d][id] ;
	int y = dp[c][d][id] ;
	if(x < y)
	{
		dp[c][d][id] = x ;
		p[c][d][id] = f ;
	}
}
void dfs(int x , int y , int id)
{
	if(x == 1 && y == 1)   return ;
	if(p[x][y][id] == 0)  dfs(x - 1 , y , id) ;
    else  dfs(x , y - 1 , id) ;
    if(p[x][y][id] == 0)  printf("D") ;
    else  printf("R") ;
}
void solve(bool flag , int x , int y)
{
	int ans = inf ;
	if(flag)  ans = 1 ;
	dp[1][1][0] = num[1][1][0] ;
	dp[1][1][1] = num[1][1][1] ;
	for(int i = 1 ; i <= n ; i ++)
	  for(int j = 1 ; j <= n ; j ++)
	  {
	  	 change(i , j , i + 1 , j , 0 , 0) ;
	  	 change(i , j , i + 1 , j , 1 , 0) ;
	  	 change(i , j , i , j + 1 , 0 , 1) ;
	  	 change(i , j , i , j + 1 , 1 , 1) ;
	  }
	int z = min(dp[n][n][0] , dp[n][n][1]) ;
	if(z >= ans)
	{
		printf("1\n") ;
		for(int i = 1 ; i <= x - 1 ; i ++)  printf("D") ;
		for(int i = 1 ; i <= y - 1 ; i ++)  printf("R") ;
		for(int i = 1 ; i <= n - x ; i ++)  printf("D") ;
		for(int i = 1 ; i <= n - y ; i ++)  printf("R") ;
		printf("\n") ;
	}
	else
	{
		printf("%d\n" , z) ;
		if(dp[n][n][0] < dp[n][n][1])  dfs(n , n , 0) ;
		else  dfs(n , n , 1) ;
		printf("\n") ;
	}  
}
int main()
{
	bool flag = 0 ;
	int x = 0 , y = 0 ;
	scanf("%d" , &n) ;
	memset(num , 0 , sizeof(num)) ;
	memset(dp , inf , sizeof(dp)) ;
	for(int i = 1 ; i <= n ; i ++)
	  for(int j = 1 ; j <= n ; j ++)
	  {
	  	 int z ;
	  	 scanf("%d" , &z) ;
	  	 if(z == 0)  flag = 1 , x = i , y = j ;
	  	 else
		 { 
		   while(z % 2 == 0)  z /= 2 , num[i][j][0] ++ ;
	  	   while(z % 5 == 0)  z /= 5 , num[i][j][1] ++ ;
		 } 
	  }
	solve(flag , x , y) ;
	return 0 ;
}

 

發佈了231 篇原創文章 · 獲贊 12 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章