zoukankan      html  css  js  c++  java
  • 普及状压选做 (持更)

    普及状压选做


    第1题

    看了题解。
    把点 (n) 作为不存在的第二起始点真是惊为天人的操作。(其实是我太菜啦

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i,l,r) for(int i=(l);i<=(r);++i)
    const int N = 13;
    
    int n,m,v[N],g[N+1][N+1];
    pair<int,int>f[1<<N][N+1][N+1];
    // f[S][i][j] 表示点集为S, 最前为i, 次前为j(不存在为0), first为最大总价值, second为数量
    // f[S][i][j] = f[S-{i}][j][k] + v[i]*v[j] + (g[i)[k]?a[i]*a[j]*a[k]:0);
    
    int main() {
        
        int q;cin>>q;while(q--) {
            memset(g,0,sizeof g);
            scanf("%d%d", &n,&m);
            rep(i,0,n-1)scanf("%d",&v[i]);
            rep(i,1,m) {int a,b; scanf("%d%d", &a,&b);
                --a;--b; g[a][b] = g[b][a] = 1;
            }
            memset(f,0x80,sizeof f);
            rep(i,0,n-1) f[1<<i][i][n] = make_pair(0,1);
            pair<int,int> ans = make_pair(0,0);
            
            rep(S,0,(1<<n)-1) {
                rep(i,0,n-1) {
                    if(!((S>>i)&1)) continue;
                    int Si = S^(1<<i);
                    rep(j,0,n-1) {
                        if(!g[i][j]) continue;
                        if(!((Si>>j)&1)) continue;
                        int Sij = Si^(1<<j)^(1<<n);
                        rep(k,0,n) {
                            if(!((Sij>>k)&1)) continue;
                            int tmp = f[Si][j][k].first + v[i]*v[j] + (g[i][k]?v[i]*v[j]*v[k]:0);
                            if(f[S][i][j].first < tmp) {
                                f[S][i][j] = make_pair(tmp, f[Si][j][k].second);
                            } else if(f[S][i][j].first == tmp) {
                                f[S][i][j].second += f[Si][j][k].second;
                            }
                        }
                    }
                }
            }
            
            rep(i,0,n-1)rep(j,0,n-1) {
                if(ans.first<f[(1<<n)-1][i][j].first) {
                    ans = f[(1<<n)-1][i][j];
                } else if(ans.first==f[(1<<n)-1][i][j].first) {
                    ans.second += f[(1<<n)-1][i][j].second;
                }
            }
            rep(i,0,n-1) ans.first += v[i];
            if(ans.second) printf("%d %d
    ", ans.first, ans.second/2);
            else printf("0 0
    ");
        }
        return 0;
    }
    

    第2题

    似乎是轮廓线的状压
    先上乘法原理, 每行的状态用 ({ 0,1 }) 表示, (1) 表示一个竖着的骨牌的上端, 其余为 (0)
    转移似乎很难想啊……但发现一次转移无非考虑在本行怎么摆竖骨牌的上端。
    配合预处理大法复杂度减少一个 (O(n)), 开心食用。
    最后复杂度是 (O(n*2^{m+1}))

    #include<bits/stdc++.h>
    using namespace std;
    #define sub(i,k) for(int i=0;i<(1<<(k));++i)
    #define rep(i,l,r) for(int i=(l);i<=(r);++i)
    const int N = 11;
    
    
    int n,m;
    long long f[N+1][1<<N];
    int good[1<<N];
    
    int main()
    {
        while(scanf("%d%d", &n, &m)==2 && n && m) {
            memset(good, 0, sizeof good);
            sub(i,m) {
                bool ok = true;
                int cnt = 0;
                rep(j,0,m-1) {
                    if((i>>j)&1) {
                        if(cnt&1) ok = false;
                        cnt=0;
                    } else {
                        ++cnt;
                    }
                }
                if(cnt&1) ok = false;
                good[i] = ok;
                // rep(j,0,m-1) cout<<((i>>j)&1);
                // cout<<' '<<ok<<'
    ';
            }
            memset(f,0,sizeof f);
            f[0][0] = 1ll;
            rep(i,1,n) sub(now,m) sub(pre,m) {
                if(now&pre) continue;
                if(!good[now|pre]) continue;
                f[i][now] += f[i-1][pre];
            }
            cout << f[n][0] << '
    ';
        }
        return 0;
    }
    

    第3题

    经典题哟。
    写了一下午自闭了。
    换了个写法20min过啦。

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i,l,r) for(int i=(l);i<=(r);++i)
    const int N = 100;
    const int M = 10;
    
    int n,m,a[N+1];
    int cnt, nodes[61], sum[61];
    long long f[2][61][61];
    
    int main() {
        m = 10;
        scanf("%d%d", &n,&m);
        rep(i,1,n)rep(j,0,m-1) { char c;cin>>c;if(c=='H')a[i]|=(1<<j); }
        rep(S,0,(1<<m)-1) {
            if(S&(S<<1)) continue;
            if(S&(S<<2)) continue;
            nodes[++cnt] = S;
            int tmp = nodes[cnt];
            while(tmp) tmp-=tmp&(-tmp), ++sum[cnt];
        }
        
        rep(i,1,cnt) {
            if(nodes[i] & a[1]) continue;
            f[1&1][i][1] = sum[i];
        }
        
        long long ans = 0ll;
        rep(i,2,n) rep(j,1,cnt) { if(nodes[j]&a[i])continue;
            rep(k,1,cnt) {
                if(nodes[k]&a[i-1])continue;
                f[i&1][j][k] = 0;
                rep(l,1,cnt) {
                    if(nodes[l]&a[i-2])continue;
                    if(nodes[j]&nodes[k]) continue;
                    if(nodes[j]&nodes[l]) continue;
                    f[i&1][j][k] = max(f[i&1][j][k], f[(i-1)&1][k][l]+sum[j]);
                }
                ans = max(ans, f[i&1][j][k]);
            }
        }
        
        cout << ans;
        
        return 0;
    }
    

    第4题

    题意就是求生成树, 因为最优解一定是原图的一个生成树。
    刚开始想了个 sb做法, 妄想把每个点的深度信息塞到状态里, 后来发现这不就是爆搜 =_=。

    参考题解, 用分层转移的手法, 转移的时候枚举当前生成树的子集作为最后一层, 然后转移, 注意要把当前集的树高塞进状态里。

    转移的时候枚举当前集的子集, 将其作为前一状态。、但是无法保证枚举出来的就是 “最后一层”, 似乎可能会出现比最优解更小的解!但是可以冷静地想一下,由于转移总是合法的(即总是生成树), 所以得到的解一定 (ge) 最优解, 而最优解一定会被找出, 所以正确性可以保证owo。

    技巧总结: 二进制数表示集合时枚举某集合的子集的方法。
    重大失误总结: 没有认识到极限数据(特别是小数据)对答案的影响。

    #include<bits/stdc++.h>
    using namespace std;
    #define rep(i,l,r) for(int i=(l);i<=(r);++i)
    
    const int N = 12;
    
    int n,m,g[N][N],trans[1<<N][1<<N];
    long long f[1<<N][N];
    
    void outS(int S) {
        puts("
    {");
        rep(i,0,n-1) if(S&(1<<i)) cout<<i<<", ";
        puts("
    }
    ");
    }
    
    int main() {
        memset(g,0x3f,sizeof g);
        memset(f,0x3f,sizeof f);
        
        scanf("%d%d", &n,&m);
        if(n==1) {
            cout << 0;
            return 0;
        }
        while(m--) {
            int u,v,w; scanf("%d%d%d", &u,&v,&w);
            --u; --v;
            g[u][v] = g[v][u] = min(g[u][v], w);
        }
        
        rep(S,0,(1<<n)-1) {
            // outS(S);
            for(int S0=(S-1)&S;S0;S0=(S0-1)&S) {
                int nS = S^S0;
                bool ok = true;
                int sum = 0;
                rep(v,0,n-1) if((1<<v)&nS) {
                    int Val = 0x3f3f3f3f;
                    rep(u,0,n-1) if((1<<u)&S0) Val = min(Val, g[u][v]);
                    if(Val == 0x3f3f3f3f) {ok=false; break;}
                    sum += Val;
                }
                if(!ok) trans[S][nS] = 0x3f3f3f3f;
                else    trans[S][nS] = sum;
            }
        }
        rep(i,0,n-1) f[1<<i][0] = 0;
        long long ans = 0x3f3f3f3f;
        rep(S,0,(1<<n)-1) {
            rep(Sdep,1,n-1) {
                for(int S0=(S-1)&S; S0; S0 = (S0-1)&S) if(trans[S][S^S0] != 0x3f3f3f3f)
                    f[S][Sdep] = min(f[S][Sdep], f[S0][Sdep-1]+Sdep*(trans[S][S^S0]));
                 
                if(S==(1<<n)-1) ans = min(ans, f[S][Sdep]);
            }
        }
        cout << ans;
        return 0;
    }
    

    第5题

    第6题

  • 相关阅读:
    SpringBoot整合JavaMail发送邮件
    SpringBoot使用SpringDataJPA完成CRUD
    开源「高逼格」简历例句
    Spring Boot 两种多数据源配置:JdbcTemplate、Spring-data-jpa
    Spring Boot
    Socket网络编程
    项目action:前台传多个dataWrap给后台
    Java——定时任务调度工具
    Spring5源码,@ModelAttribute
    windows下MongoDB的安装及配置
  • 原文地址:https://www.cnblogs.com/tztqwq/p/13259875.html
Copyright © 2011-2022 走看看