• Join The Future (剪枝 + 状态压缩)


    一道暴力搜索的恶心剪枝题目。

    先处理好某个点确定之后其他点的也确定的是谁,还有分别为什么情况,分别用vis,sta来记录。当然可以直接使用一个3进制数来表示,但是这里需要额外写一个三进制数求值的函数较为麻烦。然后写完就是搜索的问题了,搜索方向就是给点为0,1一直下去,如果没有剪枝,时间复杂度应该是O(240),显然TLE,题意给出的限制很大,我们依据它来剪枝就好了。然后就是求最小的字典序,其实只需要传进最上面那个数据就可以了,因为你已经确定了01情况,剩下的就是选择每个数的最小或者次小的数。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    
    const int maxn = 50;
    const int mod  = 1e9 + 7;
    const int inf  = 0x3f3f3f3f;
    vector<pair<int, int> >cnst[maxn];
        int T,n,m,aa,bb,cc;
    int l[maxn], r[maxn], trodi[maxn], vis[maxn];
    LL b[maxn], odd[maxn][2], cnt[maxn][2], con[maxn], dans[maxn][2], ans;
    
    ///求字典序最小的
    void update(LL vis, LL sta){
        LL pre = 0;
        bool better = false;
        for(int i = 1; i <= n; i ++){
            int now;
            if(vis & b[i]) now = l[i] + ((l[i] & 1) != ((sta >> i & 1) ^ pre));
            else now = l[i];
            if(better) trodi[i] = now;
            else{
                if(now > trodi[i]) return ;
                if(now < trodi[i]) better = true, trodi[i] = now;
            }
            pre = pre ^ (now & 1);
        }
    }
    
    ///搜索所有01情况、剪枝、求总量
    void dfs(int step, LL vis, LL sta){
        if(step > n){
            LL tmp = 0LL;
            for(int i = 0; i < 2; i ++){
                if((vis&b[n]) && (sta >> n & 1) != i) continue;
                tmp = (tmp + dans[n][i]) % mod;
            }
            if(tmp){
                update(vis, sta);
                ans = (ans + tmp) % mod;
            }
            return ;
        }
        dans[step][0] = dans[step][1] = 0;
       ///(0, 1) * (0, 1) 的四种结果,求出当前节点为0,1的个数
    for(int i = 0; i < 2; i ++){ if((vis&b[step]) && (sta >> step & 1) != i) continue; for(int j = 0; j < 2; j ++){ if((vis&b[step - 1]) && (sta >>step - 1 & 1) != j) continue; dans[step][i] = (dans[step][i] + dans[step - 1][j] * cnt[step][i ^ j]% mod) % mod; } } if(cnst[step].empty()) dfs(step + 1, vis, sta); else for(int i =0; i < 2; i ++) if(dans[step][i]) dfs(step + 1, vis | con[step], sta | odd[step][i]); } void solve(){ ///求出某个点确定了之后其他的限制情况,当某点确定之后奇偶情况其他点为奇数的点 for(int i = 0; i <= n; i ++){ odd[i][0] = odd[i][1] = con[i] = 0; memset(vis, -1, sizeof(vis)); queue<int>que;while(!que.empty())que.pop(); vis[i] = 1;que.push(i); while(!que.empty()){ int u = que.front();que.pop(); con[i] |= b[u]; odd[i][vis[u]] |= b[u]; for(auto x : cnst[u]){ int v = x.first, w = x.second; if(~vis[v]){ if((vis[u] ^ vis[v] != w)){ printf("0 -1 ");return ; } }else{ vis[v] = vis[u] ^ w; que.push(v); } } } } memset(trodi, inf, sizeof(trodi)); ans = 0LL; dans[0][0] = 1; dfs(1, con[0], odd[0][0]); if(trodi[1] == inf) printf("0 -1 "); else{ printf("%lld ",ans); for(int i = 1; i <= n; i ++) printf("%d%c", trodi[i], " "[i == n]); } } int main(){ scanf("%d",&T); for(int i = 0; i < maxn; i ++) b[i] = 1ll << i; while(T --){ scanf("%d%d",&n,&m); for(int i = 1; i <= n; i ++){ scanf("%d%d", &l[i], &r[i]); cnt[i][l[i]&1] = (r[i] - l[i]) / 2 + 1; cnt[i][(l[i]&1)^1] = r[i] - l[i] + 1 - cnt[i][l[i]&1]; } for(int i = 0; i <= n; i ++) cnst[i].clear(); for(int i = 0; i < m; i ++){ scanf("%d%d%d",&aa, &bb, &cc); cnst[aa - 1].push_back(make_pair(bb, cc)); cnst[bb].push_back(make_pair(aa - 1, cc)); } solve(); } return 0; }
    more crazy more get!
  • 相关阅读:
    若依问题解决(一)
    Java 将两个List转换为流合并List
    后端返回前端文本换行显示,只能在前端再转换
    Java Stream() 流根据对象属性去重
    vue 当前端传回后端,后端使用实体类接收数据显示报错
    js 中 getMonth() 获取的月份比现实少一个月
    vue连个数组对比
    JS链接跳转方法
    ElementUI--表格toggleRowSelection无法选中
    Linux 常用命令
  • 原文地址:https://www.cnblogs.com/wethura/p/9783083.html
Copyright © 2020-2023  润新知