前置技能二进制枚举子集
for(int j=i;j;j=(j-1)&i)
从集合本身开始
每次-1,&i保证最后的结果一定是i的子集(0的位置不会变成1)
及每次都是从左到右消去一个1
思路:二进制枚举这n个人的子集,判断哪几个状态是能够在一辆车上的,然后对于所有状态判断他的哪两个互补的子集能使得这个状态的人最少。
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<queue> #include<set> #include<map> #include<vector> #define inf 0x3f3f3f3f #define mem(a,b) memset(a,b,sizeof(a)) #define ll long long #define sd(x) scanf("%d",&(x)) #define sl(x) scanf("%lld",&(x)) #define slf(x) scanf("%lf",&(x)) #define scs(s) scanf("%s",s) #define rep(i,a,b) for(int i=a;i<=b;i++) #define per(i,a,b) for(int i=a;i>=b;i--) #define lowbit(x) x&(-x) #define ls now<<1 #define rs now<<1|1 #define lson l,mid,ls #define rson mid+1,r,rs #define All L,R using namespace std; const int maxn=3e5+10; const int mod=998244353; map<string ,int>q; string s[20]; int nmap[20][20],tot=0; int n,m,k,ans=inf,vis[maxn],tans[maxn],dp[maxn],t[35]; int cheek(int mub) { for(int i=1;i<=mub;i++) { for(int j=1;j<=mub;j++) { if(nmap[t[i]][t[j]]) return 0; } } return 1; } void dfs(int u) { if(dp[u]==1) { for(int i=0;i<n;i++) { if(((1<<i))&u) cout<<s[i+1]<<" "; } cout<<endl; return ; } dfs(u^vis[u]); dfs(vis[u]); } int main() { string a,b; sd(n),sd(m),sd(k); rep(i,1,n) cin>>s[i],q[s[i]]=i; rep(i,1,m) cin>>a>>b,nmap[q[a]][q[b]]=nmap[q[b]][q[a]]=1; int N=(1<<n)-1; rep(i,0,N) { int now=i,j=0,mub=0; mem(t,0); while(now) { if(now&1) t[++mub]=j+1; j++; now>>=1; } dp[i]=inf; if(mub>k) continue; if(cheek(mub))dp[i]=1; } for(int i=1;i<=N;i++) { for(int j=i;j;j=(j-1)&i) { if(dp[i]>dp[i^j]+dp[j]) { dp[i]=dp[i^j]+dp[j]; vis[i]=j; } } } cout<<dp[N]<<endl; dfs(N); return 0; }