E.Helping Hiasat(Codeforces Round #533 (Div. 2))(最大独立集/状压+记忆化搜索)

传送门

题意:给出事件个数,然后给出访问的人数,之后给出事件,如果是1那么可以修改名称,如果是2证明某位朋友来访,如果此为朋友来访的时候正好是他的名字,那么就开心,问最后可以最多使多少位朋友开心?

题解:对于每次1后面的朋友之间相互连线,然后最后求解最大独立集,也就是求解它补图的最大团即可。刚开始自己写了一个暴搜+1个剪枝求解最大独立集T11,最后发现红书板子传送门,感觉自己好菜,我是主要负责的图论和dp,感觉实在是菜的不行,把专题刷完。

附上代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<set>

using namespace std;

const int maxn=55;
int m,n;
bool g[maxn][maxn];
int len[maxn],ans,list[maxn][maxn],mc[maxn];
bool found;

void dfs(int size)
{
    if(len[size]==0){
        if(size>ans){
            ans=size;
            found=true;
        }
    }
    for(int k=0;k<len[size]&&!found;k++){
        if(size+len[size]-k<=ans){
            break;
        }
        int i=list[size][k];
        if(size+mc[i]<=ans){
            break;
        }
        len[size+1]=0;
        for(int j=k+1;j<len[size];j++){
            if(g[i][list[size][j]]){
                list[size+1][len[size+1]++]=list[size][j];
            }
        }
        dfs(size+1);
    }
}

void max_cluster()
{
    mc[n]=ans=1;
    for(int i=n-1;i;i--){
        found=false;
        len[1]=0;
        for(int j=i+1;j<=n;j++){
            if(g[i][j]){
                list[1][len[1]++]=j;
            }
        }
        dfs(1);
        mc[i]=ans;
    }
}

int main()
{
    cin>>m>>n;
    int cnt=0,opt;
    string s;
    map<string,int>ma;
    set<int>se;
    set<int>::iterator it1,it2;
    memset(g,true,sizeof(g));
    for(int i=1;i<=m;i++){
        cin>>opt;
        if(opt==1){
            for(it1=se.begin();it1!=se.end();it1++){
                for(it2=se.begin();it2!=se.end();it2++){
                    g[*it1][*it2]=false;
                }
            }
            se.clear();
        }else{
            cin>>s;
            if(ma.find(s)!=ma.end()){
                se.insert(ma[s]);
            }else{
                ma[s]=++cnt;
                se.insert(cnt);
            }
        }
    }
    for(it1=se.begin();it1!=se.end();it1++){
        for(it2=se.begin();it2!=se.end();it2++){
            g[*it1][*it2]=false;
        }
    }
    max_cluster();
    cout<<ans<<endl;
    return 0;
}

还可以这样做,可以将朋友用对应的位置表示,然后用g数组表示出此位置与其他位置能相容的情况,之后就是记忆化搜索+状压了,每次都是考虑目前状态头部取或者不取,取的话就得&g,由于数组范围的限制,所以只能记忆化个1<<20的范围即可。

附上代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,op;
string s;
map<string,int>M;
ll g[50],dp[1<<20];
vector<ll>V;
int dfs(ll x){
   int res=x>= (1<<20)?0:dp[x];
   if(res==0&&x){
        int j=63-__builtin_clzll(x);
        res = max( dfs(x^ (1ll<<j)) ,dfs(x& (g[j]))+1 );
   }
   if(x<(1ll<<20))dp[x] =res;
   return res;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        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);
                }
            }
        }
    }
    int ans = dfs((1ll<<m )-1);
    cout<<ans<<endl;
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值