zoukankan      html  css  js  c++  java
  • 四维dp 或者 剪枝 + dfs Codeforces Beta Round #6 (Div. 2 Only) D

    http://codeforces.com/contest/6/problem/D

    题目大意:有一队人,排成一列,每个人都有生命值,你每次可以攻击2~n位置的一个的人,假设每次攻击的位置为pos,那么pos位受到a点伤害,pos-1和pos+1受到b点伤害。问让所有人生命值都小于0所需要的最少操作数。

    思路:最近在加强dp类型的题目,但是因为做过一点IDA*,感觉这题貌似可以用IDA*差不多的思路来做哈?(关键是我不会DP,TAT)然后我们找寻一下IDA*的可行性剪枝就好了:目前数列的maxval<=(lim - deep) * a即可,否则TLE了

    这是IDA*的代码

    //看看会不会爆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
    ")
    const int maxn = 15;
    const int inf = 0x3f3f3f3f;
    int road[maxn * maxn];
    int n, a, b;
    int h[maxn];
    
    bool dfs(int pos, int deep, int lim){
    
        int maxcnt = -inf;
        for (int i = 1; i <= n; i++) maxcnt = max(maxcnt, h[i]);
        if (deep > lim) return false;
        if (maxcnt > (lim - deep) * a) return false;//加上这个剪枝就变得超快
        if (deep == lim){
            if (maxcnt >= 0)return false;
            else return true;
        }
        if (pos == n) return false;
    
        int cnt = max(h[pos - 1] / b, max(h[pos] / a, h[pos + 1] / b)) + 1;
        for (int cnt1 = 0; cnt1 <= cnt; cnt1++){
            if (h[pos - 1] >= cnt1 * b) continue;
            h[pos - 1] -= b * cnt1; h[pos] -= a * cnt1; h[pos + 1] -= b * cnt1;
            for (int j = deep + 1; j <= deep + cnt1; j++)
                road[j] = pos;
            if (dfs(pos + 1, deep + cnt1, lim)) {
                for (int j = deep + 1; j <= deep + cnt1; j++) road[j] = pos;
                return true;
            }
            h[pos - 1] += b * cnt1; h[pos] += a * cnt1; h[pos + 1] += b * cnt1;
        }
        return false;
    }
    
    int main(){
        memset(road, 0, sizeof(road));
        cin >> n >> a >> b;
        int beg = 0;
        for (int i = 1; i <= n; i++) scanf("%d", h + i);
        for (int i = 1; i <= 15 * 15; i++)
            if (dfs(2, 0, i)){
                printf("%d
    ", i);
                for (int j = 1; j <= i; j++)
                    printf("%d%c", road[j], j == i ? '
    ' : ' ');
                break;
            }
        return 0;
    }
    View Code

    明天附上DP代码。晚安~

    dp思路:

    定义dp[i][j][k][z]表示目前攻击第i个人,j表示第i-1个人的生命,k表示第i个人的生命,z表示第i+1个人的生命。然后转移需要注意一下以下方面

    ①简单的转移:dp[i][j - b][k - a][z - b] = dp[i][j][k][z];

    ②如果j-b以后的生命值为0了,那么dp[i+1][k - a][z - b][h[i + 2]]=dp[i][j][k][z] + 1;

    ③如果j本来就为0,那么dp[i+1][k][z][h[i + 2]] = dp[i][j][k][z];

    //看看会不会爆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
    ")
    const int maxn = 30;
    const int inf = 0x3f3f3f3f;
    struct Point{
        int i, j, k, z;
        Point(int i = 0, int j = 0, int k = 0, int z = 0): i(i), j(j), k(k), z(z){}
    }par[maxn][maxn][maxn][maxn];
    int dp[maxn][maxn][maxn][maxn];
    int n, a, b;
    int h[maxn];
    
    int main(){
        cin >> n >> a >> b;
        for (int i = 1; i <= n; i++){
            scanf("%d", h + i); h[i]++;
        }
        memset(par, -1, sizeof(par));
        memset(dp, 0x3f, sizeof(dp));
        dp[2][h[1]][h[2]][h[3]] = 0;
        for (int i = 2; i <= n - 1; i++){
            for (int j = h[i - 1]; j >= 0; j--){
                for (int k = h[i]; k >= 0; k--){
                    for (int z = h[i + 1]; z >= 0; z--){
                        int nj = max(0, j - b), nk = max(0, k - a), nz = max(0, z - b);
                        if (dp[i][nj][nk][nz] > dp[i][j][k][z] + 1){
                            dp[i][nj][nk][nz] = dp[i][j][k][z] + 1;
                            par[i][nj][nk][nz] = Point(i, j, k, z);
                        }
                        if (nj == 0 && dp[i + 1][nk][nz][h[i + 2]] > dp[i][j][k][z] + 1){
                            dp[i + 1][nk][nz][h[i + 2]] = dp[i][j][k][z] + 1;
                            par[i + 1][nk][nz][h[i + 2]] = Point(i, j, k, z);
                        }
                        if (j == 0 && dp[i + 1][k][z][h[i + 2]] > dp[i][j][k][z]){
                            dp[i + 1][k][z][h[i + 2]] = dp[i][j][k][z];
                            par[i + 1][k][z][h[i + 2]] = par[i][j][k][z];
                        }
                    }
                }
            }
        }
        int ans = dp[n][0][0][0];
        printf("%d
    ", ans);
        Point tmp = Point(n, 0, 0, 0);
        while (ans--){
            tmp = par[tmp.i][tmp.j][tmp.k][tmp.z];
            printf("%d ", tmp.i);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    读 Zepto 源码之内部方法
    读Zepto源码之代码结构
    vue-auto-focus: 控制自动聚焦行为的 vue 指令
    vue-lazy-render: 延迟渲染大组件,增强页面切换流畅度
    用vue实现模态框组件
    谷歌插件Image downloader开发之popup
    关于const
    Python线程指南(转自AstralWind)
    PyQt中的图形绘制
    sizeof和strlen之间的区别
  • 原文地址:https://www.cnblogs.com/heimao5027/p/5988770.html
Copyright © 2011-2022 走看看