Codeforces Beta Round #2_B. The least round way(DP)

题目传送门:http://codeforces.com/contest/2/problem/B


题型:动态规划、数论


题意:

      给出n*n的矩阵,从左上角走到右下角,只能向右走或者向下走,将路线上的值全部乘起来,问结果末尾0最少的路线。


分析:

      乘积的末尾0,取决于这些数的质因子中的2和5的对数,即决定与最少的2的个数或者是最少的5的个数。若对矩阵中的每个元素进行质因子分解,复杂度为O(n^2*sqrt(n))过大。由于只要2的个数和5的个数,那么只要对每个元素不断除2或者除5即可,复杂度为O(n^2*log(n))可以接受。

      采用DP进行路径的寻找,找出含2最少的路径和含5最少的路径,状态转移方程为:

                                           dp(i,j) = min(dp(i-1,j),dp(i,j-1)) + num(i,j);

      可以证明末尾0最少的路径是这两条路径中的一条,证明如下:

      反证法:假设有一条路径比这两条更优。

                   所以这条路径的2和5的对数最少,所以相比与其他路径,该路径上必然2最少或者5最少。

                   然而根据题设,

                   这条路径含2的个数和含5的个数都不是最少的。

                   矛盾。

                   所以2最少的路或者5最少的路最优。     得证。

        由于2最少的路径上的末尾0等于2的个数,5最少的路径上的末尾0等于5的个数,所以

                                         ANS = min(dp2(n,n),dp5(n,n));

        路径只需逆退即可。

        需要注意的是,如果矩阵元素中有0且ANS>1,那么结果为1,路径就是经过该元素所在点的任意路径。


代码:

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#define INF 0x3f3f3f3f
using namespace std;

int n;
int num2[1010][1010];
int num5[1010][1010];
int dp2[1010][1010];
int dp5[1010][1010];
char str[1234];
int _x,_y;

void init(){
	memset(num2,0,sizeof(num2));
	memset(num5,0,sizeof(num5));
	for(int i=0;i<=n;i++){
		dp2[i][0] = dp2[0][i] = 0;
		dp5[i][0] = dp5[0][i] = 0;
	}
}

void FindRoute2(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==1 && j==1){
				dp2[i][j] = num2[i][j];
				continue;
			}
			if(i==1){
				dp2[i][j] = dp2[i][j-1] + num2[i][j];
				continue;
			}
			if(j==1){
				dp2[i][j] = dp2[i-1][j] + num2[i][j];
				continue;
			}
			dp2[i][j] = min(dp2[i-1][j],dp2[i][j-1]) + num2[i][j];
		}
	}
}

void FindRoute5(){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==1 && j==1){
				dp5[i][j] = num5[i][j];
				continue;
			}
			if(i==1){
				dp5[i][j] = dp5[i][j-1] + num5[i][j];
				continue;
			}
			if(j==1){
				dp5[i][j] = dp5[i-1][j] + num5[i][j];
				continue;
			}
			dp5[i][j] = min(dp5[i-1][j],dp5[i][j-1]) + num5[i][j];
			continue;
		}
	}
}

void Ask2(){
	int pre = 0;
	int i=n,j=n;
	while(i!=1 || j!=1){
		if(dp2[i][j-1]<dp2[i-1][j]){
			str[pre] = 'R';
			j--;
		}
		else{
			str[pre] = 'D';
			i--;
		}
		pre++;
	}
	for(int i=pre-1;i>=0;i--){
		printf("%c",str[i]);
	}
	puts("");
}

void Ask5(){
	int pre = 0;
	int i=n,j=n;
	while(i!=1 || j!=1){
		if(dp5[i][j-1]<dp5[i-1][j]){
			str[pre] = 'R';
			j--;
		}
		else{
			str[pre] = 'D';
			i--;
		}
		pre++;
	}
	for(int i=pre-1;i>=0;i--){
		printf("%c",str[i]);
	}
	puts("");
}

void Special(int xx,int yy){
	for(int i=2;i<=xx;i++){
		printf("D");
	}
	for(int i=2;i<=n;i++){
		printf("R");
	}
	for(int i=xx+1;i<=n;i++){
		printf("D");
	}
	puts("");
}

int main(){
	while(~scanf("%d",&n)){
		bool ts = false;
		init();
		int a,tmp;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				scanf("%d",&a);
				if(a==0){
					_x = i;
					_y = j;
					ts = true;
				}
				tmp = a;
				while(tmp%2==0&&tmp>0){
					num2[i][j]++;
					tmp/=2;
				}
				tmp = a;
				while(tmp%5==0&&tmp>0){
					num5[i][j]++;
					tmp/=5;
				}
			}
		}

		FindRoute2();
		FindRoute5();
		int ans = min(dp2[n][n],dp5[n][n]);
		if(ts && ans>1){
			puts("1");
			Special(_x,_y);
			continue;
		}
		for(int i=0;i<=n;i++){
			dp2[i][0] = dp2[0][i] = INF;
			dp5[i][0] = dp5[0][i] = INF;
		}
		printf("%d\n",ans);
		if(dp2[n][n]<dp5[n][n]) Ask2();
		else Ask5();

	}

	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值