【BZOJ4405】【WC2016】挑战NPC(带花树)

题面

BZOJ

洛谷

Uoj

Description

小N最近在研究NP完全问题,小O看小N研究得热火朝天,便给他出了一道这样的题目:

有n个球,用整数1到n编号。还有m个筐子,用整数1到m编号。

每个筐子最多能装3个球。

每个球只能放进特定的筐子中。具体有e个条件,第i个条件用两个整数vi和ui描述,表示编号为vi的球可以放进编号为ui的筐子中。

每个球都必须放进一个筐子中。如果一个筐子内有不超过1个球,那么我们称这样的筐子为半空的。

求半空的筐子最多有多少个,以及在最优方案中,每个球分别放在哪个筐子中。

小N看到题目后瞬间没了思路,站在旁边看热闹的小I嘿嘿一笑:“水题!”

然后三言两语道出了一个多项式算法。

小N瞬间就惊呆了,三秒钟后他回过神来一拍桌子:

“不对!这个问题显然是NP完全问题,你算法肯定有错!”

小I浅笑:“所以,等我领图灵奖吧!”

小O只会出题不会做题,所以找到了你——请你对这个问题进行探究,并写一个程序解决此题。

Input

第一行包含1个正整数T,表示有T组数据。

对于每组数据,第一行包含3个正整数n,m,e,表示球的个数,筐子的个数和条件的个数。

接下来e行,每行包含2个整数vi,ui,表示编号为vi的球可以放进编号为ui的筐子。

Output

对于每组数据,先输出一行,包含一个整数,表示半空的筐子最多有多少个。

Sample Input

1

4 3 6

1 1

2 1

2 2

3 2

3 3

4 3

Sample Output

2

HINT

对于所有数据,T≤5,1≤n≤3m。保证 1≤vi≤n,1≤ui≤m,且不会出现重复的条件。

保证至少有一种合法方案,使得每个球都放进了筐子,且每个筐子内球的个数不超过 3。

M<=100

题解

考虑一下可以放的球数和对答案的贡献:

3个球:1

2个球:1

1个球:0

0个球:0

我们发现就是可以放的球数整除\(2\)的结果

所以,把一个篮子拆成三个

一个球还是一个球

如果一个球可以放进一个篮子里,证明着球可以与篮子拆分出来的任意一个点匹配

现在要求的相当于篮子自身能够匹配的最大数目

如果超过了两个空,那么就可以自身与自身匹配了,从而产生贡献一。

所以考虑如下构图:

每个篮子是三个点,点与点之间互相连边

每个球是一个点,可以向它可以放的篮子拆出来的三个点连边。

这样的话,因为保证所有球都有匹配,

所以最大匹配数=球数+有贡献的篮子自身的匹配

所以最后的答案就是最大匹配数-球数。

至于如何计算方案,一定优秀增广球,再增广篮子,否则会出现篮子自身优先匹配,然后球没有匹配的情况,到时方案错误(虽然答案也是对的。。。)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 1111
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
struct Line{int v,next;}e[MAX*MAX];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
queue<int> Q;
int dfn[MAX],tim;
int match[MAX],pre[MAX];
int f[MAX],vis[MAX],n,m,E;
int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
int lca(int u,int v)
{
++tim;u=getf(u);v=getf(v);
while(dfn[u]!=tim)
{
dfn[u]=tim;
u=getf(pre[match[u]]);
if(v)swap(u,v);
}
return u;
}
void Blossom(int x,int y,int w)
{
while(getf(x)!=w)
{
pre[x]=y,y=match[x];
if(vis[y]==2)vis[y]=1,Q.push(y);
if(getf(x)==x)f[x]=w;
if(getf(y)==y)f[y]=w;
x=pre[y];
}
}
bool Aug(int S)
{
for(int i=1;i<=n+m+m+m;++i)f[i]=i,vis[i]=pre[i]=0;
while(!Q.empty())Q.pop();Q.push(S);vis[S]=1;
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;
if(getf(u)==getf(v)||vis[v]==2)continue;
if(!vis[v])
{
vis[v]=2;pre[v]=u;
if(!match[v])
{
for(int x=v,lst;x;x=lst)
lst=match[pre[x]],match[x]=pre[x],match[pre[x]]=x;
return true;
}
vis[match[v]]=1,Q.push(match[v]);
}
else
{
int w=lca(u,v);
Blossom(u,v,w);
Blossom(v,u,w);
}
}
}
return false;
}
void init()
{
memset(h,0,sizeof(h));cnt=1;
memset(dfn,0,sizeof(dfn));tim=0;
memset(match,0,sizeof(match));
}
int main()
{
int T=read();
while(T--)
{
n=read();m=read();E=read();
init();int ans=0;
for(int i=1;i<=m;++i)
{
Add(i,i+m);Add(i+m,i);
Add(i,i+m+m);Add(i+m+m,i);
Add(i+m,i+m+m);Add(i+m+m,i+m);
}
for(int i=1;i<=E;++i)
{
int v=read(),u=read();
Add(v+m+m+m,u);Add(u,v+m+m+m);
Add(v+m+m+m,u+m);Add(u+m,v+m+m+m);
Add(v+m+m+m,u+m+m);Add(u+m+m,v+m+m+m);
}
for(int i=m+m+m+n;i;--i)if(!match[i]&&Aug(i))++ans;
printf("%d\n",ans-n);
for(int i=1;i<=n;++i)printf("%d ",(match[i+m+m+m]-1)%m+1);puts("");
}
return 0;
}

【BZOJ4405】【WC2016】挑战NPC(带花树)的更多相关文章

  1. BZOJ 4405 [wc2016]挑战NPC 带花树 一般图最大匹配

    https://www.lydsy.com/JudgeOnline/problem.php?id=4405 这道题大概就是考场上想不出来,想出来也调不出来的题. 把每个桶拆成三个互相有边的点,每个球向 ...

  2. [bzoj4405][wc2016]挑战NPC

    来自FallDream的博客,未经允许,请勿转载,谢谢. 小N最近在研究NP完全问题,小O看小N研究得热火朝天,便给他出了一道这样的题目: 有n个球,用整数1到n编号.还有m个筐子,用整数1到m编号. ...

  3. [WC2016]挑战NPC(一般图最大匹配)

    [WC2016]挑战NPC(一般图最大匹配) Luogu 题解时间 思路十分有趣. 考虑一个筐只有不多于一个球才有1的贡献代表什么. 很明显等效于有至少两个位置没有被匹配时有1的贡献. 进而可以构造如 ...

  4. [BZOJ]4405: [wc2016]挑战NPC(带花树)

    带花树模板 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; ...

  5. bzoj 4405: [wc2016]挑战NPC【带花树】

    把每个筐子拆成3个,分别表示放0/1/2个,然后把这三个点两两连起来,每一个可以放在筐里的球都想这三个点连边. 这样可以发现,放0个球的时候,匹配数为1,放1个球的时候,匹配数为1,放2个球的时候,匹 ...

  6. [WC2016]挑战NPC

    Sol 这做法我是想不到\(TAT\) 每个筐子拆成三个相互连边 球向三个筐子连边 然后跑一般图最大匹配 这三个筐子间最多有一个匹配 那么显然每个球一定会放在一个筐子里,一定有一个匹配 如果筐子间有匹 ...

  7. [UOJ171][WC2016]挑战NPC

    uoj luogu bzoj sol 你可以列一个表格. 一个框子里放球的数量 0 1 2 3 对"半空框子"数量的贡献 1 1 0 0 把一个框子拆三个点.两两之间连边. 会发现 ...

  8. P4258-[WC2016]挑战NPC【带花树】

    正题 题目链接:https://www.luogu.com.cn/problem/P4258 题目大意 给出\(n\)个球,\(m\)个篮筐,每个球都可以被放入一些特定的篮筐,每个球都要放,要求球的个 ...

  9. 「WC2016」挑战NPC

    「WC2016」挑战NPC 解题思路 这个题建图非常厉害,带花树什么的只会口胡根本写不动,所以我写了机房某大佬教我的乱搞. 考虑把一个筐 \(x\) 拆成 \(x1,x2,x3\) 三个点,且这三个点 ...

随机推荐

  1. 初识JMM

    目录 what is JMM? JMM变量存储结构 JMM三大特性 原子性 可见性 有序性 java 堆栈 静态存储 栈式存储 堆式存储 JVM是啥 参考<Inside JVM> what ...

  2. Django——POST请求及Action触发事件

    添加网页login,将类型置为post,并添加action page,也就是之前写好的页面 添加page网页的views函数,要求获取post指令,如果username及password均正确则跳转到 ...

  3. Python解包参数列表及 Lambda 表达式

    解包参数列表 当参数已经在python列表或元组中但需要为需要单独位置参数的函数调用解包时,会发生相反的情况.例如,内置的 range() 函数需要单独的 start 和 stop 参数.如果它们不能 ...

  4. 二叉树的深度<java版>

    二叉树的结构 二叉树是比较常见的一种的一种数据结构. 首先看看二叉树的数据结构: //由左节点和右节点以及一个节点值构成 public class TreeNode{ TreeNode leftNod ...

  5. PytorchZerotoAll学习笔记(三)--自动求导

    Pytorch给我们提供了自动求导的函数,不用再自己再推导计算梯度的公式了 虽然有了自动求导的函数,但是这里我想给大家浅析一下:深度学习中的一个很重要的反向传播 references:https:// ...

  6. 0329--Scrum团队准备工作

    一.团队名称,团队目标.团队口号.团队照 1.团队名称:Blackhriar 2.团队目标:完成一个完善的,可以投入市场供用户使用,甚至具有一定商业价值的项目~come on! 3.团队口号:抱怨事件 ...

  7. Scrum立会报告+燃尽图(06)选题

    此作业要求参见:[https://edu.cnblogs.com/campus/nenu/2018fall/homework/2195] 一.小组介绍 组长:王一可 组员:范靖旋,王硕,赵佳璐,范洪达 ...

  8. “Hello World!”团队第七次Scrum立会

    "Hello world!"团队召开第七次Scrum立会.博客内容: 1.会议时间 2.会议成员 3.会议地点 4.会议内容 5.Todo list 6.会议照片 7.燃尽图 一. ...

  9. 敏捷开发 Scrum 综述

    敏捷开发 Scrum 综述 这一星期学习了敏捷开发,然后阅读了相关的书籍,从网上查找了很多相关的资料,对敏捷开发scrum有了更加深刻了理解,对敏捷开发做了如下总结: 一.什么是敏捷开发? 敏捷开发提 ...

  10. “我爱淘”第二冲刺阶段Scrum站立会议6

    完成任务: 完成学院分类的点击查看书籍功能,可以点击书的条目查看书的详细信息. 计划任务: 将书的详细信息进行完善,并且可以点击收藏以及已预订等功能. 遇到问题: 分类功能,根据不同学院,自动将数据库 ...