CF1185F Two Pizzas

洛谷评测传送门

题目描述

A company of nn friends wants to order exactly two pizzas. It is known that in total there are 99 pizza ingredients in nature, which are denoted by integers from 11 to 99 .

Each of the nn friends has one or more favorite ingredients: the ii -th of friends has the number of favorite ingredients equal to f_if**i ( 1 \le f_i \le 91≤f**i≤9 ) and your favorite ingredients form the sequence b_{i1}, b_{i2}, \dots, b_{if_i}b**i1,b**i2,…,bif**i ( 1 \le b_{it} \le 91≤bit≤9 ).

The website of CodePizza restaurant has exactly mm ( m \ge 2m≥2 ) pizzas. Each pizza is characterized by a set of r_jr**j ingredients a_{j1}, a_{j2}, \dots, a_{jr_j}a**j1,a**j2,…,ajr**j ( 1 \le r_j \le 91≤r**j≤9 , 1 \le a_{jt} \le 91≤ajt≤9 ) , which are included in it, and its price is c_jc**j .

Help your friends choose exactly two pizzas in such a way as to please the maximum number of people in the company. It is known that a person is pleased with the choice if each of his/her favorite ingredients is in at least one ordered pizza. If there are several ways to choose two pizzas so as to please the maximum number of friends, then choose the one that minimizes the total price of two pizzas.

输入格式

The first line of the input contains two integers nn and mm ( 1 \le n \le 10^5, 2 \le m \le 10^51≤n≤105,2≤m≤105 ) — the number of friends in the company and the number of pizzas, respectively.

Next, the nn lines contain descriptions of favorite ingredients of the friends: the ii -th of them contains the number of favorite ingredients f_if**i ( 1 \le f_i \le 91≤f**i≤9 ) and a sequence of distinct integers b_{i1}, b_{i2}, \dots, b_{if_i}b**i1,b**i2,…,bif**i ( 1 \le b_{it} \le 91≤bit≤9 ).

Next, the mm lines contain pizza descriptions: the jj -th of them contains the integer price of the pizza c_jc**j ( 1 \le c_j \le 10^91≤c**j≤109 ), the number of ingredients r_jr**j ( 1 \le r_j \le 91≤r**j≤9 ) and the ingredients themselves as a sequence of distinct integers a_{j1}, a_{j2}, \dots, a_{jr_j}a**j1,a**j2,…,ajr**j ( 1 \le a_{jt} \le 91≤ajt≤9 ).

输出格式

Output two integers j_1j1 and j_2j2 ( 1 \le j_1,j_2 \le m1≤j1,j2≤m , j_1 \ne j_2j1=j2 ) denoting the indices of two pizzas in the required set. If there are several solutions, output any of them. Pizza indices can be printed in any order.

题意翻译

【题目描述】

现在到了午饭时间,你要为nn个朋友定披萨。

众所周知,披萨的原料分为9种。每位朋友都有自己喜好的原料(一种或多种),第ii个朋友喜欢的原料有f_if**i种,分别是b_{i1},b{i2},...,b{if_i}b**i1,b**i2,...,bif**i

披萨店出售mm种不同的披萨。第jj种披萨里面有r_jr**j种原料,分别是a_{j1},a_{j2},...,a_{jr_j}a**j1,a**j2,...,ajr**j。第jj种披萨售价为c_jc**j

现在你们决定购买恰好两个披萨。我们称一个人是“满意的”,当且仅当他/她想要的每种原料都在至少一个买下来的披萨出现。你希望最多人是“满意的”,并在这个前提下,支出越少越好。请输出任意一种方案。

【输入格式】

第一行,两个正整数n,mn,m,分别代表人数和披萨种数。

接下来nn行,每行描述一个人的口味,先是一个正整数f_if**i,接下来f_if**i个正整数b_{i1},b{i2},...,b{if_i}b**i1,b**i2,...,bif**i,含义如题所示。

接下来mm行,每行描述一种披萨。先是两个正整数c_j,r_jc**j,r**j,接下来r_jr**j个正整数a_{j1},a_{j2},...,a_{jr_j}a**j1,a**j2,...,ajr**j,含义如题所示。

【输出格式】

一行,两个正整数j_1.j_2j1.j2,代表所买的两个披萨。以任意顺序输出任意方案均可。

【数据范围与约定】

保证:

  • 1\leq n\leq 10^5,2\leq m\leq 10^51≤n≤105,2≤m≤105
  • 1\leq c_j\leq 10^91≤c**j≤109
  • 1\leq f_i,b_{it},r_j,a_{jt}\leq 91≤f**i,bit,r**j,ajt≤9

输入输出样例

输入 #1复制

输出 #1复制

输入 #2复制

输出 #2复制

输入 #3复制

输出 #3复制

题解:

2019.11.8模拟赛T2 30分场

前一天刚学状压就来一道状压的题,虽然没场上切但还是感谢出题人@ysy20021208,吸一口大佬欧气。

一开始看到数据范围果断选择了状压,一看就是这9种东西选还是没选,然后觉得可以依次枚举这\(m\)张披萨两两搭配合并之后能满足多少人的欲望。进行更新。一看这复杂度是\(O(N*M*M)\)的,肯定是废只能拿30分。后来一想:虽然\(n\)是\(10^5\)级别的,但是这\(9\)位的状态无论如何只能搭配出\(512(2^9)\)种可能,所以\(N\)就被我们优化到了\(512\),然后一顿乱搞,最后还是拿了30分(滑稽)

(暴力考场代码使用了各种\(C++STL\)...)

#include<cstdio>
#include<bitset>
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=1e5+10;
const int INF=2147483645;
int n,m,maxx,minn=INF,ansx,ansy,tot;
bitset<10> t;
bitset<10> s[1<<9+1];
vector<pair<int,bitset<10> > >vec;
struct node
{
int v,id;
bitset<10> x;
}p[maxn];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int a,f;
bitset<10> tt;
scanf("%d",&a);
for(int j=1;j<=a;j++)
{
int k;
scanf("%d",&k);
tt.set(k);
}
for(int j=0;j<vec.size();j++)
if(vec[j].second==tt)
{
vec[j].first++;
break;
}
vec.push_back(make_pair(1,tt));
}
for(int i=1;i<=m;i++)
{
int a;
scanf("%d%d",&p[i].v,&a);
for(int j=1;j<=a;j++)
{
int k;
scanf("%d",&k);
p[i].x.set(k);
}
p[i].id=i;
}
maxx=-1;
for(int i=1;i<=m;i++)
for(int j=i;j<=m;j++)
{
tot=0;
if(i==j)
continue;
t=(p[i].x|p[j].x);
for(int k=0;k<vec.size();k++)
if((vec[k].second&t)==vec[k].second)
tot+=vec[k].first;
if(tot>=maxx)
{
if(tot>maxx)
minn=INF;
maxx=tot;
if((p[i].v+p[j].v)<minn)
{
ansx=i,ansy=j;
minn=p[i].v+p[j].v;
}
}
}
printf("%d %d",ansx,ansy);
return 0;
}

然后我详细介绍一下看完题解之后自己捋顺的满分思路:

其实暴力想的时候已经离正解好近了(真是遗憾)。

我想到了把\(n\)个人的状态进行包含性质的枚举,但是就是没想到\(m\)张披萨也可以这么干(简直匪夷所思)。

看一下题:我们发现:对于两张披萨,它们有可能能满足同一个人的需求,这时,这两张披萨之间肯定是子集或真子集的关系。那么我们完全可以把\(m\)张披萨也“需求化”。什么意思呢?既然枚举所有的\(n,m\)复杂度过高,那么我们就“要什么拿什么”,设置\(minn[st]\)数组来存能满足需求为\(st\)的人的所有披萨中价值最小的那张的花费。并且,存储\(num[st]\)表示需求为\(st\)的人的人数。同时,因为我们需要选择最优秀的两张披萨,所以还需要记录一下满足需求为\(st\)的人的所有披萨中价值第二小的花费为\(minn2[st]\)。

那么我们在对披萨们进行预处理的时候,就可以顺道把这两个数组处理出来。并且,因为我们最终要输出披萨的编号,所以我们还需要另开数组\(k,k2\)来存当前状态最小价值披萨的编号、次小价值披萨的编号。

然后我们就完成了暴力的优化。由原来的\(O(m^2)\)级别的复杂度成功降低到了\(O(512\times 512)\)的复杂度。所以,在我们状态压缩的时候,懂得优化复杂度,一定要从状态入手,看枚举的东西能不能按状态进一步压缩而变得更快。

AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=1e5+10;
const int maxs=1<<9;
int n,m;
int a[maxn],b[maxn],v[maxn],num[maxs];
int minn[maxs],minn2[maxs],k[maxs],k2[maxs];
int ans[maxs],ansx[maxs],ansy[maxs];
int main()
{
memset(minn,0x3f,sizeof(minn));
memset(minn2,0x3f,sizeof(minn2));
memset(ans,0x7f,sizeof(ans));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int p;
scanf("%d",&p);
for(int j=1;j<=p;j++)
{
int k;
scanf("%d",&k);
a[i]|=1<<(k-1);
}
num[a[i]]++;
}
for(int i=1;i<=m;i++)
{
int p;
scanf("%d%d",&v[i],&p);
for(int j=1;j<=p;j++)
{
int k;
scanf("%d",&k);
b[i]|=1<<(k-1);
}
if(v[i]<minn[b[i]])
{
minn2[b[i]]=minn[b[i]];
minn[b[i]]=v[i];
k2[b[i]]=k[b[i]];
k[b[i]]=i;
}
else if(v[i]<minn2[b[i]])
{
minn2[b[i]]=v[i];
k2[b[i]]=i;
}
}
for(int i=0;i<maxs;i++)
if(k[i]&&k2[i])
{
ans[i]=minn[i]+minn2[i];
ansx[i]=k2[i],ansy[i]=k[i];
}
for(int i=0;i<maxs;i++)
for(int j=i+1;j<maxs;j++)
{
if(minn[i]>1000000000 || minn[j]>1000000000)
continue;
ans[i|j]=min(ans[i|j],minn[i]+minn[j]);
if(ans[i|j]==minn[i]+minn[j])
{
ansx[i|j]=k[i];
ansy[i|j]=k[j];
}
}
for(int i=0;i<maxs;i++)
printf("%d\n",ans[i]);
int target=0,maxx=-1,mi=2100000000;
for(int i=0;i<maxs;i++)
{
if(ans[i]>2000000000)
continue;
int tmp=0;
for(int j=0;j<maxs;j++)
if((j&i)==j)
tmp+=num[j];
if(tmp>maxx)
target=i,maxx=tmp,mi=ans[i];
else if(tmp==maxx && ans[i]<mi)
target=i,mi=ans[i];
}
printf("%d %d\n",ansx[target],ansy[target]);
return 0;
}

CF1185F Two Pizzas的更多相关文章

  1. CF1185F Two Pizzas 状压

    你发现 pizza 种类数不会很多,状压一下就可以了 code: #include <bits/stdc++.h> #define M 11 #define N 100005 #defin ...

  2. Django模型类Meta元数据详解

    转自:https://my.oschina.net/liuyuantao/blog/751337 简介 使用内部的class Meta 定义模型的元数据,例如: from django.db impo ...

  3. ANNOTATION PROCESSING 101 by Hannes Dorfmann — 10 Jan 2015

    原文地址:http://hannesdorfmann.com/annotation-processing/annotationprocessing101 In this blog entry I wo ...

  4. django model Meta选项

    可用的 Meta 选项 abstract Options.abstract 如果 abstract = True ,这个 model 就是一个 抽象基类 . app_label Options.app ...

  5. What Is Mathematics?

    What Is Mathematics? The National Council of Teachers of Mathematics (NCTM), the world's largest org ...

  6. poj3311 Hie with the Pie (状态压缩dp,旅行商)

    Hie with the Pie Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 3160   Accepted: 1613 ...

  7. 状态压缩 DP

    D - Hie with the Pie Crawling in process... Crawling failed Time Limit:2000MS     Memory Limit:65536 ...

  8. 【Django】Django model与数据库操作对应关系(转)

    Django对数据库的操作分用到三个类:Manager.QuerySet.Model. Manager的主要功能定义表级方法(表级方法就是影响一条或多条记录的方法),我们可以以models.Manag ...

  9. Head First Design Patterns

    From Head First Design Patterns. Design Principle: Idnetify the aspects of your application that var ...

随机推荐

  1. C# 常用排序算法

    文章引用地址:https://www.cnblogs.com/fengyeqingxiang/archive/2019/06/14/11021852.html C#所有经典排序算法汇总   1 2 3 ...

  2. Java哲学家进餐问题|多线程

    Java实验三 多线程 哲学家进餐问题: 5个哲学家共用一张圆桌,分别坐在周围的5张椅子上, 在圆桌上有5个碗和5只筷子(注意是5只筷子,不是5双), 碗和筷子交替排列.他们的生活方式是交替地进行思考 ...

  3. python第一次作业-Numpy练习

    1.创建一个边界值为1而内部都是0的数组,图例如下:[提示:]解此题可以先把所有值都设置为1,这是大正方形:其次,把边界除外小正方形全部设置为0.本题用到numpy的切片原理.多维数组同样遵循x[st ...

  4. Python连载6-time包函数简介

    一.接连载5中time模块 1.函数:altzone (1)含义:获取当前时间与UTC时间相差的秒数,再有夏令时的情况下. (2)格式:time.altzone 2.函数:daylight (1)含义 ...

  5. 一份完整的PyCharm图解教程

    PyCharm 是一种 Python IDE,可以帮助程序员节约时间,提高生产效率.那么具体如何使用呢?本文从 PyCharm 安装到插件.外部工具.专业版功能等进行了一一介绍,希望能够帮助到大家. ...

  6. springboot-热部署Jrebel

    1. 场景描述 介绍下idea+springboot下的热部署插件-Jrebel,贼好用,以前用过好多种,但是总出现不稳定或者会莫名其妙的没有部署新代码. 2.解决方案 springboot自带的de ...

  7. pytest框架与unittest框架的对比

    一.pytest的优势 pytest是基于unittest之上的单元测试框架,它的优势如下: 自动发现测试模块和测试方法 断言使用 assert + 表达式 可以设置测试会话级(session).模块 ...

  8. HDU 6556 (2018CCPC吉林 B题)

    ### HDU 6556 题目链接 ### 题目大意: 给你四个国家的时区,告诉你 A 国家的时间,让你输出这时候在 B 国家的时间,还需要输出对于 A 国家来说这是 昨天.今天 还是 明天. 分析前 ...

  9. 使用ScriptX控件实现IE浏览器分页打印功能

    之前讲过js调用ie浏览器自带打印的用法,今天讲使用插件的方式.浏览器自带打印不能控制页边距.页眉页脚等选项,尤其是如果分页打印的话,无法自动将前一页标题带到本页,所以不适用多页打印的功能.使用Scr ...

  10. C++入门到理解阶段二基础篇(3)——C++数据类型

    目录 1.数据类型概述 2.基本的内置类型 整型 实型(浮点型) 字符型 转义字符 字符串型 c风格的字符串 c++风格的字符串 布尔类型bool 1.数据类型概述 使用编程语言进行编程时,需要用到各 ...