数论+dp Codeforces Beta Round #2 B

http://codeforces.com/contest/2/problem/B

题目大意:给你一个n*n的矩形,问从(1,1)出发到(n,n),把图中经过的所有的数字都乘在一起,最后这个数字有多少个0?

思路:经过分析,只有2和5出现的时候才会有0.所以我们预处理把这个数包含的所有的2和5都给拿出来就好了。但是我发现如果每次转移都要统计2和5的个数的话,状态就炸了,所以我只想到了这里TAT。后来看了一下题解以后发现,只需要知道目前到这个位置以后最小的2(或5)的个数就好了。

然后转移我也想了好半天。。。于是还是看了。。。2333(我好菜啊)

转移就是只需要知道最后最小的个数是2还是5,然后再通过该数字去转移就好了

 

于是早上+中午+下午3个小时就过去了= =

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
const int maxn = 1000 + 5;
const LL inf = 1e17;
LL a[maxn][maxn];
pair<LL, LL> p[maxn][maxn];
LL dp[maxn][maxn][2];
int n;
/*
定义dp[i][j]表示到(i,j)所经过的0的最少的个数
0只在2*5的时候出现,所以只需要统计2和5的个数即可
以上思路是行不通的,因为这样子的话dfs或者dp都会有两个变量,所以会超级难写(TAT我写了一个早上)
所以我们要找一下两者当中的共同点。我们只需要找目前状态的2或5的最大值就好了
*/
pair<LL, LL> cal(LL val){
    pair<LL, LL> cnt = mk(0, 0);
    LL tmp = val;
    while (val % 2 == 0 && val) {cnt.fi++; val /= 2;}
    val = tmp;
    while (val % 5 == 0 && val) {cnt.se++; val /= 5;}
    return cnt;
}
vector<char> v;

bool dfs(int x, int y, int k){///0 is first, 1 is second
    //printf("x = %d y = %d\n", x, y);
    if (x > n || y > n || x < 1 || y < 1) return false;
    if (x == 1 && y == 1) return true;
    if (dp[x - 1][y][k] < dp[x][y - 1][k]){
        if (dfs(x - 1, y, k)) {v.push_back('D'); return true;}
    }
    else {
        if (dfs(x, y - 1, k)) {v.push_back('R'); return true;}
    }
    return false;
}

int main(){
    cin >> n;
    memset(p, -1, sizeof(p));
    bool flag = false;
    pair<int, int> zero;
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= n; j++){
            scanf("%lld", &a[i][j]);
            p[i][j] = cal(a[i][j] == 0 ? 10 : a[i][j]);///当做10,先消去0的影响
            if (a[i][j] == 0) {flag = true; zero = mk(i, j);}
        }
    }
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= n; j++){
            for (int k = 0; k < 2; k++)
                dp[i][j][k] = inf;
            if (p[i][j].fi == -1) p[i][j] = mk(inf, inf);
        }
    dp[1][1][0] = p[1][1].fi, dp[1][1][1] = p[1][1].se;
    for (int i = 1; i <= n; i++){
        for (int j = 1; j <= n; j++){
            if (i == 1 && j == 1) continue;
            dp[i][j][0] = min(dp[i - 1][j][0], dp[i][j - 1][0]) + p[i][j].fi;
            dp[i][j][1] = min(dp[i - 1][j][1], dp[i][j - 1][1]) + p[i][j].se;
        }
    }
/*
    haha;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
            printf("%lld%c", min(dp[i][j][0], dp[i][j][1]), j == n ? '\n' : ' ');
*/
    LL ans = min(dp[n][n][0], dp[n][n][1]);
    if (flag && ans >= flag){
        ans = 1LL * flag;
        printf("%lld\n", ans);
        int cnt = 0;
        for (int i = 2; i <= zero.fi; i++) printf("D"), cnt++;
        for (int i = 1; i < n; i++) printf("R"), cnt++;
        for (int i = zero.fi + 1; i<= n; i++) printf("D"), cnt++;
        cout << endl;
        return 0;
    }
    printf("%lld\n", ans);
    dfs(n, n, dp[n][n][0] > dp[n][n][1]);
    for (int i = 0; i < v.size(); i++)
        printf("%c", v[i]);
    cout << endl;
    return 0;
}

/*
4
1 10 10 10
1  0  1 10
10 10 2 10
1  10 1 1
*/
View Code

 

posted @ 2016-10-22 15:33  知る奇迹に  阅读(275)  评论(0编辑  收藏  举报