題目鏈接: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);
}
}