Codeforces Beta Round #2 B. The least round way DP + 数论
http://codeforces.com/problemset/problem/2/B
题意:
给定n*n的一个数字矩阵,求从左上角(1,1)走到右下角(n,n)将每个数字相乘,使得乘机中包含的0最少。要求只能往下走或者往右走。
思路:
状态转移方程很好看,关键是怎么保证所得乘积所含0个数最少。想一下如何才能使乘积中出现0呢?只有质因子2*5才能出现0,其余的质因子相乘不会出现。所以我们只要保证使得2与5的个数凑出来的10最少即可,即2与5的个数最少即可 我们肯定得到一条路线使得ans = min(2num,5num); ans的值在所有路线里是最小的。即得到了所得答案。
开始我同时枚举的路线上的2与5的个数,样例能过但是wa,其实这样写转移方程不对的。我们是要的是min(2num,5num),我们怎样保证2最少的同时保证5最少。。不好弄。
后来看了一下解题报告,我们分2与5讨论,一条路线保证2最少 第二条路线保证5最少 那么第一条的5num肯定大于第二条路线5num 第二条的2num肯定大于第一条的2num
这样我们只要去min(ans1,ans2)即可。因为再走别的路线肯定会出现大于第一条路线的2num大于第二条路线5num。还有一个trick就死如果矩阵中出现0这样我们只要保证路过0就可以得到1的值,结果就为min(ans1,ans2,1)了。
才开始自己写的在处理1的时候,原点(1,1)我都给输出了,二逼啊。。。导致错了好多次。。。。
View Code
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 1073741824 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 150 #define N 1007 using namespace std; //freopen("data.in","r",stdin); struct point{ int x,y; }pre[2][N][N];//记录父亲节点 int dp[2][N][N];//0表示2的路径,1表示5的路径 int zx,zy; int mat[2][N][N]; char path[4*N];//记录路径 int n; int solve(int mk){ int i,j; pre[mk][1][1].x = pre[mk][1][1].y = -1; dp[mk][1][1] = mat[mk][1][1]; for (i = 1; i <= n; ++i){ for (j = 1; j <= n; ++j){ if (i == 1 && j == 1) continue; if (i == 1){ dp[mk][i][j] = dp[mk][i][j - 1] + mat[mk][i][j]; pre[mk][i][j].x = i; pre[mk][i][j].y = j - 1; } else if (j == 1){ dp[mk][i][j] = dp[mk][i - 1][j] + mat[mk][i][j]; pre[mk][i][j].x = i - 1; pre[mk][i][j].y = j; } else{ int tp1 = dp[mk][i - 1][j]; int tp2 = dp[mk][i][j - 1]; if (tp1 < tp2){ dp[mk][i][j] = dp[mk][i - 1][j] + mat[mk][i][j]; pre[mk][i][j].x = i - 1; pre[mk][i][j].y = j; } else{ dp[mk][i][j] = dp[mk][i][j - 1] + mat[mk][i][j]; pre[mk][i][j].x = i; pre[mk][i][j].y = j - 1; } } } } return dp[mk][n][n]; } int main(){ // freopen("data.in","r",stdin); int i,j; int x; bool flag = false; scanf("%d",&n); for (i = 1; i <= n; ++i){ for (j = 1; j <= n; ++j){ scanf("%d",&x); if (x == 0){//记录0的位置 zx = i; zy = j; flag = true; continue; } mat[0][i][j] = mat[1][i][j] = 0; //枚举2,5质因子的个数 int tmp = x; while (tmp %2 == 0){ mat[0][i][j]++; tmp /= 2; } tmp = x; while (tmp % 5 == 0){ mat[1][i][j]++; tmp /= 5; } } } //puts("*********"); int ans1 = solve(0); int ans2 = solve(1); int ans,mk; if (ans1 < ans2){ ans = ans1; mk = 0; } else{ ans = ans2; mk = 1; } //0的特殊处理 if (flag && ans > 1){ printf("1\n"); //注意这里从2开始,我二逼的从1开始了,wa了无数次啊。。伤不起啊 for (i = 2; i <= zy; ++i) printf("R"); for (i = 2; i <= zx; ++i) printf("D"); for (i = zy + 1; i <= n; ++i) printf("R"); for (i = zx + 1; i <= n; ++i) printf("D"); } else{ printf("%d\n",ans); int len = 0; int dx = n, dy = n; int px = pre[mk][dx][dy].x ,py = pre[mk][dx][dy].y; CL(path,0); while (px != -1 && py != -1){ if (px + 1 == dx){ path[len++] = 'D'; } else if (py + 1 == dy){ path[len++] = 'R'; } dx = px; dy = py; px = pre[mk][dx][dy].x; py = pre[mk][dx][dy].y; // puts(">>>>>>"); } for (i = len - 1; i >= 0; --i) printf("%c",path[i]); printf("\n"); } return 0; }
再贴一下处理输出的函数节约内存的代码:
View Code
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <stack> #include <set> #include <map> #include <string> #define CL(a,num) memset((a),(num),sizeof(a)) #define iabs(x) ((x) > 0 ? (x) : -(x)) #define Min(a,b) (a) > (b)? (b):(a) #define Max(a,b) (a) > (b)? (a):(b) #define ll long long #define inf 0x7f7f7f7f #define MOD 1073741824 #define lc l,m,rt<<1 #define rc m + 1,r,rt<<1|1 #define pi acos(-1.0) #define test puts("<------------------->") #define maxn 100007 #define M 150 #define N 1007 using namespace std; //freopen("data.in","r",stdin); int g[2][N][N]; int dp[2][N][N]; int zx,zy; int mat[2][N][N]; char path[4*N]; int n; int solve(int mk){ int i,j; g[mk][1][1] = 0; dp[mk][1][1] = mat[mk][1][1]; for (i = 1; i <= n; ++i){ for (j = 1; j <= n; ++j){ if (i == 1 && j == 1) continue; if (i == 1){ dp[mk][i][j] = dp[mk][i][j - 1] + mat[mk][i][j]; g[mk][i][j] = 1; } else if (j == 1){ dp[mk][i][j] = dp[mk][i - 1][j] + mat[mk][i][j]; g[mk][i][j] = 0; } else{ int tp1 = dp[mk][i - 1][j]; int tp2 = dp[mk][i][j - 1]; if (tp1 < tp2){ dp[mk][i][j] = dp[mk][i - 1][j] + mat[mk][i][j]; g[mk][i][j] = 0; } else{ dp[mk][i][j] = dp[mk][i][j - 1] + mat[mk][i][j]; g[mk][i][j] = 1; } } } } return dp[mk][n][n]; } void Path(int mk,int x,int y){ if (x == 1 && y == 1) return ; else if (g[mk][x][y] == 0){ Path(mk,x - 1,y); printf("D"); } else{ Path(mk,x,y - 1); printf("R"); } } int main(){ //freopen("din.txt","r",stdin); int i,j; int x; bool flag = false; scanf("%d",&n); for (i = 1; i <= n; ++i){ for (j = 1; j <= n; ++j){ mat[0][i][j] = mat[1][i][j] = 0; scanf("%d",&x); if (x == 0){ zx = i; zy = j; flag = true; continue; } int tmp = x; while (tmp % 2 == 0){ mat[0][i][j]++; tmp /= 2; } tmp = x; while (tmp % 5 == 0){ mat[1][i][j]++; tmp /= 5; } } } int ans1 = solve(0); int ans2 = solve(1); int ans,mk; if (ans1 < ans2){ ans = ans1; mk = 0; } else{ ans = ans2; mk = 1; } if (flag && ans > 1){ printf("1\n"); for (i = 2; i <= zx; ++i) printf("D"); for (i = 2; i <= n; ++i) printf("R"); for (i = zx + 1; i <= n; ++i) printf("D"); printf("\n"); } else{ printf("%d\n",ans); Path(mk,n,n); printf("\n"); } return 0; }