• UVA Live Archive 4490 Help Bubu(状压dp)


    难点在于状态设计,从左向右一本书一本书的考虑,每本书的决策有两种拿走或者留下,

    对于拿走后的书,之后要放回,但是决策过程中不知道到往哪里放,

    虽然前面的书的种类确定,可能是往后面放更优,而后面的书的类型还不确定。对于所有拿出来的书,最后会增加多少段只和书的种类有关。

    所以我们用s记录留下书的种类,等到所有书都考虑完了以后一并放回。

    对于留下的书,是否成为新的一段和上一次书的高度有关,因此用一个last记录上一次书的种类。(用s来判断一下last不存在的情况)

    dp[i = 第i本书][j = 拿了j次][s = 剩下书的种类][last = 上一次书的种类] = 最小混乱度

    转移方程见代码

    直接for是最好写的

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 101, maxs = 1<<8;
    int dp[2][maxn][maxs][8];
    int bc[maxs];
    int h[maxn];
    const int INF = 0x3f3f3f3f;
    
    //#define LOCAL
    int main()
    {
    #ifdef LOCAL
        freopen("in.txt","r",stdin);
    #endif
        for(int i = maxs; i-- ;){
            int x = i;
            while(x){;
                bc[i] += x&1;
                x >>= 1;
            }
        }
        int ks = 0, n, k;
        while(scanf("%d%d",&n,&k),n+k){
            int All = 0;
            for(int i = 0; i < n; i++){
                scanf("%d",h+i);
                h[i] -= 25;
                All |= 1<<h[i];
            }
            memset(dp[0],0x3f,sizeof(dp[0]));
            dp[0][0][1<<h[0]][h[0]] = 1;
            dp[0][1][0][0] = 0;
            for(int i = 1; i < n; i++){
                int a = i&1, b = a^1, bs = 1<<h[i];
                memset(dp[a],0x3f,sizeof(dp[a]));
                for(int j = k; j >= 0; j--){
                    for(int s = maxs; s--; ){
                        for(int ls = 8; ls--; ){
                            if(dp[b][j][s][ls] < INF){
                                dp[a][j][s|bs][h[i]] = min(dp[a][j][s|bs][h[i]], dp[b][j][s][ls] + (s?( h[i] == ls?0:1 ):1));
                                if(j < k) dp[a][j+1][s][ls] = min(dp[a][j+1][s][ls], dp[b][j][s][ls]);
                            }
                        }
                    }
                }
            }
            int a = n&1^1;
            int ans = n+1;
            for(int s = maxs; s--;){
                for(int ls = 8; ls--; ){
                    ans = min(ans, dp[a][k][s][ls] + bc[All^s]);
                }
            }
            printf("Case %d: %d
    
    ",++ks,ans);
        }
        return 0;
    }

    实际上可能有很多状态访问不到的时候,for会枚举到一些无用的状态,这时候可以考虑把有效的状态保存下来,(记忆化搜索也可以避免访问无用状态,但是没办法用滚动数组了)

    这样的话不需要对整个dp数组初始化,取代的是需要状态判重。

    这样写有种做搜索题的感觉。

    #include<bits/stdc++.h>
    using namespace std;
    
    const int maxn = 101, maxs = 1<<8;
    int dp[2][maxn][8][maxs];
    int vis[maxn][8][maxs],clk;
    
    int h[maxn];
    
    const int nil = 0;
    struct state
    {
        int ct,ls,s;
    }vc[2][maxn*8*maxs];
    
    int sz[2];
    #define dim(x) [x.ct][x.ls][x.s]
    #define add(id,ct,ls,s) vc[id][sz[id]++] = state{ct,ls,s};
    #define psb(id) vc[id][sz[id]++] = y;
    #define clr(id) sz[id] = 0;
    #define edof(id) vc[id][sz[id]]
    
    #define updata(v)
    if(vis Td != clk){
        vis Td = clk;
        dp[a] Td = v;
        psb(a);
    }else {
        dp[a] Td = min(dp[a] Td, v);
    }
    
    inline int bc(int x)
    {
        int re = 0;
        while(x){
            re += x&1;
            x >>= 1;
        }
        return re;
    }
    
    //#define LOCAL
    int main()
    {
    #ifdef LOCAL
        freopen("in.txt","r",stdin);
    #endif
        int ks = 0, n, k;
        while(scanf("%d%d",&n,&k),n+k){
            int All = 0;
            for(int i = 0; i < n; i++){
                scanf("%d",h+i);
                h[i] -= 25;
                All |= 1<<h[i];
            }
            clr(0);
            state y = {0,h[0],1<<h[0]};
            dp[0] dim(y) = 1;
            psb(0)
            y = {1,nil,0};
            dp[0] dim(y) = 0;
            psb(0)
            for(int i = 1; i < n; i++){
                int a = i&1, b = a^1, bs = 1<<h[i];
                clr(a)
                clk++;
                for(int j = 0; j < sz[b]; j++){
                    auto &x = vc[b][j];
                    int val = dp[b] dim(x);
                    int tmp = val + ( x.s? ( (x.ls == h[i])?0:1 ) : 1 );
                    y = {x.ct,h[i],x.s|bs};
                    #define Td [y.ct][y.ls][y.s]
                    updata(tmp)
                    if(x.ct < k){
                        y = {x.ct+1, x.ls, x.s};
                        updata(val)
                    }
                }
            }
            int a = n&1^1;
            int ans = n+1;
            for(int j = 0; j < sz[a]; j++){
                auto &x = vc[a][j];
                ans = min(ans, dp[a] dim(x) + bc(All^x.s));
            }
            printf("Case %d: %d
    
    ",++ks,ans);
        }
        return 0;
    }
  • 相关阅读:
    word2vec原理推导与代码分析
    vim 删除
    HanLP 配置与使用
    python import 其他 package的模块
    mysql 修改root密码
    Spring Boot 整合 PageHelper
    MyBatis SQL语句构建器
    webpack4
    MySql DCL数据控制语言(对用户权限的设置)
    MySql DQL数据查询语言
  • 原文地址:https://www.cnblogs.com/jerryRey/p/4857886.html
Copyright © 2020-2023  润新知