题目链接:https://codeforces.com/contest/1185/problem/F
题目大意:给出n个人,m个蛋糕。每个人有自己喜欢的口味(多种).每个蛋糕有自己的口味(多种).每个蛋糕有自己的价格c。当一个人可以吃到所有满足自己口味的食物时,会感到开心(从不同的蛋糕中凑够也算)。问满足最多的人开心,的最少花费的组合时什么。
思路:由于口味最多只有9种。所以我们很容易的用一个int数来储存。由于最多(1<<10)-1种口味,所以我们可以进行预处理出来,每种口味可以满足多少人O( (1<<10)n )。然后枚举口味的组合O( (1<<10)*(1<<10) )种组合。对于每种组合,取最小花费即可。
由于必须买两个蛋糕,注意都不能满足的情况(样例3)。
Update:
跟学长聊天的时候说到了这道题,学长说有一种更快速的方法处理出来每个情况的满足人的数量:SOS DP。复杂度O(10*n)
比我的预处理复杂度O( (1<<10)n )感觉要快好多(但是CF上体现出来的只有100ms的差别。。CF牛逼!。
所以学习一下SOS DP:https://blog.csdn.net/weixin_38686780/article/details/100109753
ACCode1:
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand((unsigned)time(NULL));rand();
#include<map>//unordered_map
#include<set>//multiset
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
#define ll long long
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=1e5+10;
//const int MAXM=10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
ll Cnt[MAXN],Cost1[MAXN],Id1[MAXN],Cost2[MAXN],Id2[MAXN];
int A[MAXN],B[MAXN];
int n,m;
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=0;i<=(1<<10);++i) Cnt[i]=0,Cost1[i]=Cost2[i]=INF64;
for(int i=1;i<=n;++i) A[i]=B[i]=0;
for(int i=1;i<=n;++i){
int cnt;scanf("%d",&cnt);
for(int j=1;j<=cnt;++j){
int x;scanf("%d",&x);
A[i]|=(1<<x);
}
}
for(int i=0;i<=(1<<10)-1;++i){//遍历所有的组合情况,看能够提供多少人
for(int j=1;j<=n;++j){
if((A[j]&i)==A[j]){
Cnt[i]++;
}
}
}
for(int i=1;i<=m;++i){
int val,cnt;scanf("%d%d",&val,&cnt);
for(int j=1;j<=cnt;++j){
int x;scanf("%d",&x);
B[i]|=(1<<x);
}
if(Cost1[B[i]]>val){//花费最少
Cost2[B[i]]=Cost1[B[i]];Id2[B[i]]=Id1[B[i]];
Cost1[B[i]]=val;Id1[B[i]]=i;
}
else if(Cost2[B[i]]>val){//花费次少
Cost2[B[i]]=val;Id2[B[i]]=i;
}
}
ll maxcnt=0,mincost=INF64;PII ans;
for(int i=0;i<=(1<<10)-1;++i){//枚举组合
for(int j=0;j<=(1<<10)-1;++j){
if(Cost1[i]==INF64||Cost1[j]==INF64) continue ;
if(i==j&&Cost2[i]==INF64) continue ;
int kind=i|j;
int cnt=Cnt[kind];
// cout<<"i="<<bitset<10>(i)<<"j="<<bitset<10>(j)<<"kind="<<bitset<10>(kind)<<"cnt="<<cnt<<endl;
//printf("i=%d j=%d kind=%d cnt=%d\n",i,j,kind,cnt);
if(cnt>maxcnt){
maxcnt=cnt;
if(i==j) ans=make_pair(Id1[i],Id2[j]),mincost=Cost1[i]+Cost2[j];
else ans=make_pair(Id1[i],Id1[j]),mincost=Cost1[i]+Cost1[j];
}
else if(cnt==maxcnt){
int tmpcost;
if(i==j) tmpcost=Cost1[i]+Cost2[i];
else tmpcost=Cost1[i]+Cost1[j];
// printf("tmpcost=%lld\n",tmpcost);
if(tmpcost<mincost){
mincost=tmpcost;
if(i==j) ans=make_pair(Id1[i],Id2[j]);
else ans=make_pair(Id1[i],Id1[j]);
}
}
}
}printf("%d %d\n",ans.first,ans.second);
}
}
UpDate ACCode2:
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand((unsigned)time(NULL));rand();
#include<map>//unordered_map
#include<set>//multiset
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
#define ll long long
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=1e5+10;
//const int MAXM=10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
ll Cnt[MAXN],Cost1[MAXN],Id1[MAXN],Cost2[MAXN],Id2[MAXN];
int A[MAXN],B[MAXN];
int n,m;
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i=0;i<=(1<<10);++i) Cnt[i]=0,Cost1[i]=Cost2[i]=INF64;
for(int i=1;i<=n;++i) A[i]=B[i]=0;
for(int i=1;i<=n;++i){
int cnt;scanf("%d",&cnt);
for(int j=1;j<=cnt;++j){
int x;scanf("%d",&x);
A[i]|=(1<<x);
}Cnt[A[i]]++;//初始化数量
}
for(int i=0;i<=9;++i){//SOSDP找最符合要求的人
for(int j=(1<<10)-1;j>=0;--j){
if((1<<i)&j) Cnt[j]=Cnt[j]+Cnt[j^(1<<i)];
}
}
// for(int i=0;i<=(1<<10)-1;++i){//遍历所有的组合情况,看能够提供多少人
// for(int j=1;j<=n;++j){
// if((A[j]&i)==A[j]){
// Cnt[i]++;
// }
// }
// }
for(int i=1;i<=m;++i){
int val,cnt;scanf("%d%d",&val,&cnt);
for(int j=1;j<=cnt;++j){
int x;scanf("%d",&x);
B[i]|=(1<<x);
}
if(Cost1[B[i]]>val){//花费最少
Cost2[B[i]]=Cost1[B[i]];Id2[B[i]]=Id1[B[i]];
Cost1[B[i]]=val;Id1[B[i]]=i;
}
else if(Cost2[B[i]]>val){//花费次少
Cost2[B[i]]=val;Id2[B[i]]=i;
}
}
ll maxcnt=0,mincost=INF64;PII ans;
for(int i=0;i<=(1<<10)-1;++i){//枚举组合
for(int j=0;j<=(1<<10)-1;++j){
if(Cost1[i]==INF64||Cost1[j]==INF64) continue ;
if(i==j&&Cost2[i]==INF64) continue ;
int kind=i|j;
int cnt=Cnt[kind];
// cout<<"i="<<bitset<10>(i)<<"j="<<bitset<10>(j)<<"kind="<<bitset<10>(kind)<<"cnt="<<cnt<<endl;
//printf("i=%d j=%d kind=%d cnt=%d\n",i,j,kind,cnt);
if(cnt>maxcnt){
maxcnt=cnt;
if(i==j) ans=make_pair(Id1[i],Id2[j]),mincost=Cost1[i]+Cost2[j];
else ans=make_pair(Id1[i],Id1[j]),mincost=Cost1[i]+Cost1[j];
}
else if(cnt==maxcnt){
int tmpcost;
if(i==j) tmpcost=Cost1[i]+Cost2[i];
else tmpcost=Cost1[i]+Cost1[j];
// printf("tmpcost=%lld\n",tmpcost);
if(tmpcost<mincost){
mincost=tmpcost;
if(i==j) ans=make_pair(Id1[i],Id2[j]);
else ans=make_pair(Id1[i],Id1[j]);
}
}
}
}printf("%d %d\n",ans.first,ans.second);
}
}