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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值