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 ;
}

 

发布了215 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Irving0323/article/details/104148372