CF-1185F-Two Pizzas(思維+二進制暴力,枚舉)(SOS DP)

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章