【训练题57:贪心 + dp】The least round way | CF2B

题意

  • The least round way | CF2B
    2000 2000 2000
    给定 n × n n\times n n×n 的矩阵,数字 a i , j a_{i,j} ai,j
    开始在左上角,每次向下 / 向右走一格,走大右下角,求所有数字的乘积的末尾 0 0 0 的个数的最小值
    你需要输出这样的一条路径
  • 1 ≤ n ≤ 1 0 3 1\le n\le 10^3 1n103
    0 ≤ a i , j ≤ 1 0 9 0\le a_{i,j}\le 10^9 0ai,j109

思路

  • 题目可以转化为,每个位置有 2 2 2 的因子的个数 a a a 个,有 5 5 5 的因子的个数 b b b
    你需要走一条路,让 min ⁡ { ∑ a , ∑ b } \min\{\sum_a,\sum_b\} min{a,b} 最小
    之前的题是让 ∑ a × ∑ b \sum_a\times\sum_b a×b 最大,感觉稍微有点题意类似吧…
  • 貌似直接搞不是很好做,但是我们可以把原问题化成两个子问题:
    d p 1 [ i ] [ j ] dp1[i][j] dp1[i][j] 表示走到 ( i , j ) (i,j) (i,j) min ⁡ { ∑ a } \min\{\sum_a\} min{a}
    d p 2 [ i ] [ j ] dp2[i][j] dp2[i][j] 表示走到 ( i , j ) (i,j) (i,j) min ⁡ { ∑ b } \min\{\sum_b\} min{b}
    最后答案就是 min ⁡ { d p 1 [ n ] [ n ] , d p 2 [ n ] [ n ] } \min\{dp1[n][n],dp2[n][n]\} min{dp1[n][n],dp2[n][n]}
  • 诶?我 可能会怀疑,路径只能走一条,但是你求 ∑ a \sum_a a 最小值的路可能和求 ∑ b \sum_b b 最小值的路是不同的,怎么会合法呢?
    但是仔细一想,因为我们的答案一定为 min ⁡ { d p 1 [ n ] [ n ] , d p 2 [ n ] [ n ] } \min\{dp1[n][n],dp2[n][n]\} min{dp1[n][n],dp2[n][n]} ,因此要么 ∑ a \sum_a a 取到最小值(此时不管 ∑ b \sum_b b 多大我们的答案都不变),要么 相反的情况
    所以我们用 f a [ i ] [ j ] [ 2 ] fa[i][j][2] fa[i][j][2] 记录两种道路的前驱,就可以去走了
  • 当然题目还有,那就是有可能 a i , j = 0 a_{i,j}=0 ai,j=0
    因为是数字乘积,走到 0 0 0 当然中间不管怎么走我们最后的答案都是 1 1 1 了,最后和我们之前的答案特判一下哪个小即可
    注意还有一个坑点 (但是没有注意到这个也能过,样例水了),我们之前求 ∑ a \sum_a a 的路上,如果遇到了 0 0 0 ,那么明显最后值为 0 0 0 了,所以对于所有值为 0 0 0 的点,我们让它们位置的 a = inf ⁡ , b = inf ⁡ a=\inf,b=\inf a=inf,b=inf,不然可以有 H a c k Hack Hack 样例的:
3
2 2 0
5 0 5
0 2 2
/**
应该输出 1,但是不修改则会输出 0
*/

代码

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL);
using namespace std;
typedef long long ll;
void show(){std::cerr << endl;}template<typename T,typename... Args>void show(T x,Args... args){std::cerr << "[ " << x <<  " ] , ";show(args...);}

const int MAX = 1e3+50;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const double EPS = 1e-5;

int dp[MAX][MAX][2];
int shu[MAX][MAX][2];
int fa[MAX][MAX][2];
bool zero;
int zx,zy;
int main()
{

    int n;n = read();
    for(int i = 0;i <= n;++i)
    for(int j = 0;j <= n;++j)
        dp[i][j][0] = dp[i][j][1] = INF;
    for(int i = 1;i <= n;++i)
    for(int j = 1;j <= n;++j){
        int t = read();
        if(t == 0){
            zero = true;
            zx = i;
            zy = j;
            shu[i][j][0] = INF;
            shu[i][j][1] = INF;
            continue;
        }
        while(t % 2 == 0){
            shu[i][j][0]++;
            t /= 2;
        }
        while(t % 5 == 0){
            shu[i][j][1]++;
            t /= 5;
        }
    }
    dp[0][1][0] = dp[0][1][1] = 0;
    for(int i = 1;i <= n;++i)
    for(int j = 1;j <= n;++j){
        dp[i][j][0] = min(dp[i-1][j][0],dp[i][j-1][0]) + shu[i][j][0];
        dp[i][j][1] = min(dp[i-1][j][1],dp[i][j-1][1]) + shu[i][j][1];

        if(dp[i-1][j][0] < dp[i][j-1][0])fa[i][j][0] = 1;
        else fa[i][j][0] = 2;

        if(dp[i-1][j][1] < dp[i][j-1][1])fa[i][j][1] = 1;
        else fa[i][j][1] = 2;
    }
    int ans = min(dp[n][n][0],dp[n][n][1]);
    int op = dp[n][n][0] < dp[n][n][1] ? 0 : 1;
    if(zero && ans > 1){
        cout << 1 << endl;
        for(int i = 1;i < zx;++i)cout << "D";
        for(int i = 1;i < zy;++i)cout << "R";
        for(int i = zx;i < n;++i)cout << "D";
        for(int i = zy;i < n;++i)cout << "R";
        return 0;
    }else{
        stack<char>S;
        int x = n,y = x;
        while(1){
            if(fa[x][y][op] == 2){
                S.push('R');
                y--;
            }else{
                S.push('D');
                x--;
            }
            if(x == 1 && y == 1)break;
        }
        cout << ans << endl;
        while(!S.empty()){
            cout << S.top();
            S.pop();
        }
    }


    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值