zoukankan      html  css  js  c++  java
  • cf 1105 e 位操作 状压dp(记忆化搜索)

    链接:https://codeforces.com/contest/1105/problem/E

    题意:有m个人(<=40),申请访问主页,如果每次主页是它的名字,他会很开心。n次操作,每次当为1的时候可以设置主页。

    思路:这样可以看做一个图,把每个人看做一个点,那么在每一次访问主页有冲突的,那么这些点是不能共同存在的。

      在最后找一个最大的独立集。

    做法 : 位操作+记忆化搜索

    开始处理 g[x]:  用二进制表示他与其他点的关系,1代表可以共存,0代表不可以

    之后从(1<<m )-1开始记忆化搜索,有两种情况,

    (1)对这个J保留,则要&g[j] 结果+1继续搜下去。

    (2)不保留,则 &(1<<j)

    细节:位操作是个很细腻的东西啊。。以及记忆化搜索的时候,因为

    数组开的不能太大,值开到了1<<20,那么开始的时候就尽量消去权重大的1

    #include<bits/stdc++.h>
    using namespace std;
    
    #define bug printf("??");
    #define ll long long
    #define pb push_back
    #define mp make_pair
    #define fi first
    #define se second
    #define all(v) v.begin(),v.end()
    #define mem(a) memset(a,0,sizeof(a))
    
    const int N = 2e5+4;
    const ll mod =1e9+7;
    const int INF = 1e9+4;
    const double eps = 1e-7;
    ll dp[1<<20];
    vector<ll>V;
    ll g[55];
    
    int dfs(ll x){
       int res=  x>= (1<<20)?0:dp[x];
       if(res==0 && x){
            //这样是把最前面的1去掉  因为记忆化只在1到1<<20
            //所以优先去掉 权重大的1
            int j=63-__builtin_clzll(x);
            //  int j = __builtin_ctzll(x)  这样是把最尾的1去掉
            res = max( dfs( x^ (1ll<<j)) ,dfs( x& (g[j]))+1 );
       }
       if(x<(1ll<<20)) dp[x] =res;
       return res;
    }
    
    int main(){
        int n,m;
        ios::sync_with_stdio(0);
        cin.tie(0);
    
        cin>>n>>m;
        map<string ,int >M;
        string s;
        int cnt= 0 ;int op;
        for(int i=1;i<=n;++i){
            //scanf("%d",&op);
            cin>>op;
            if(op==1) V.push_back(0);
            else {
                cin>>s;
                if(M.find(s)==M.end())
                    M.insert({s, M.size()});
                //这里用二进制保存所有的点 为了之后处理
                V.back() |= (1ll<<M[s]);
            }
        }
    
    
        for(int i=0;i<m;++i) g[i]= (1ll<<m) -1;
        for(int i=0;i<m;++i) g[i] &= ~(1ll<<i);
        for(int i=0;i<V.size();++i){
            for(int a=0;a<m;++a){
                for(int b=0;b<m;++b){
                    if(  ((V[i]>>a)&1) && ((V[i]>>b)&1) ){
                        g[a]&= ~(1ll<<b);
                    }
                }
            }
        }
        /*for(ll vi : V)
            for(int i=0; i<m; ++i)
                for(int j=0; j<m; ++j)
                    if(vi>>i&1&&vi>>j&1)
                        g[i]|=1ll<<j;
        for(int i=0; i<m; ++i)
            g[i]=~g[i];*/
        int ans = dfs(  (1ll<<m )-1);
        cout<<ans<<endl;
        return 0;
    }
  • 相关阅读:
    帮助C#菜鸟进入SQL/XML开发
    汉字转换为拼音的函数
    水晶报表的使用技巧
    用DataSet操作XML
    frame,iframe,frameset 的区别(来源网络)
    oracle 数据库锁表解决方法
    c#日期类型的使用 (转)
    深入了解ViewState 深入了解ViewState
    js中top、parent、frame
    SQL中 inner join、 left join 、right join、 outer join之间的区别(来自百度自用)
  • 原文地址:https://www.cnblogs.com/wjhstudy/p/10300349.html
Copyright © 2011-2022 走看看