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; }