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

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

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

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

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

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

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

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

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

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

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

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

输入格式

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

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

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

输出格式

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

然后再输出一行,包含 \(n\) 个整数 \(p1,p2,…,pn\) ,相邻整数之间用空格隔开,表示一种最优解。其中 \(pi\) 表示编号为 \(i\) 的球放进了编号为 \(pi\) 的筐子。如果有多种最优解,可以输出其中任何一种。

样例一

input

1

4 3 6

1 1

2 1

2 2

3 2

3 3

4 3

output

2

1 2 3 3

样例二

见样例数据下载。

限制与约定

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

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

各测试点满足以下约定:

测试点编号 $m$ 约定
$1$ $\leq 10$ $n \leq 20,e \leq 25$
$2$
$3$ $\leq 100$ $e=nm$
$4$ 存在方案使得有 $m$ 个半空的筐子
$5$ 不存在有半空的筐子的方案
$6$
$7$
$8$
$9$
$10$

时间限制: 1s

空间限制: 256MB

题解

思路极其巧妙

我们把一个框分成三个点,并且把其中两个点连上一条边

那么一个球与一个框相连就变成了这样:

我们对这个图跑一遍带花树

那么如果只有一个球放进一个框中,这个框对应的三个点就只会有一个被匹配;而剩下的两个点因为有边相连,所以一定会相互匹配

也就是说,半空的框分出的三个点中一定在两个点相互匹配

因为题目保证球全部可以放进框中,所以匹配数一定大于等于 \(n\) ,而多出来的就是这些框分出的点自己的匹配

我们只要得出有多少自己的匹配,就知道有多少半空的框,用匹配的结果减去 \(n\) 就是答案了

要使答案最大,那么匹配的结果越大越好,所以就是跑带花树

#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
#define ball(a,b) ((a-1)*3+b+n)
const int MAXN=300+10,MAXM=100+10,inf=0x3f3f3f3f;
int e,n,m,fa[MAXN<<1],beg[MAXN<<1],level[MAXN<<1],to[MAXN*MAXN*8],nex[MAXN*MAXN*8],link[MAXN<<1],pre[MAXN<<1],clk,vis[MAXN<<1];
std::queue<int> q;
template<typename T> inline void read(T &x)
{
T data=0,w=1;
char ch=0;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
x=data*w;
}
template<typename T> inline void write(T x,char ch='\0')
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
if(ch!='\0')putchar(ch);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline int found(int x)
{
if(fa[x]!=x)fa[x]=found(fa[x]);
return fa[x];
}
inline void insert(int x,int y)
{
to[++e]=y;
nex[e]=beg[x];
beg[x]=e;
to[++e]=x;
nex[e]=beg[y];
beg[y]=e;
}
inline int LCA(int u,int v)
{
for(clk++;;std::swap(u,v))
if(u)
{
u=found(u);
if(level[u]==clk)return u;
level[u]=clk;u=pre[link[u]];
}
}
inline void blossom(int u,int v,int lca)
{
while(found(u)!=lca)
{
pre[u]=v,v=link[u];
if(vis[v]==2)vis[v]=1,q.push(v);
if(found(u)==u)fa[u]=lca;
if(found(v)==v)fa[v]=lca;
u=pre[v];
}
}
inline int bfs(int s)
{
for(register int i=1;i<=n+3*m;++i)fa[i]=i;
memset(pre,0,sizeof(pre));
memset(vis,0,sizeof(vis));
while(!q.empty())q.pop();
vis[s]=1;
q.push(s);
while(!q.empty())
{
int x=q.front();
q.pop();
for(register int i=beg[x];i;i=nex[i])
{
if(found(x)==found(to[i])||vis[to[i]]==2)continue;
if(!vis[to[i]])
{
vis[to[i]]=2;
pre[to[i]]=x;
if(!link[to[i]])
{
for(register int p=to[i],las;p;p=las)las=link[pre[p]],link[p]=pre[p],link[pre[p]]=p;
return 1;
}
vis[link[to[i]]]=1;q.push(link[to[i]]);
}
else
{
int lca=LCA(x,to[i]);
blossom(x,to[i],lca);blossom(to[i],x,lca);
}
}
}
return 0;
}
int main()
{
static int T,l,ans;
read(T);
while(T--)
{
e=ans=0;
memset(beg,0,sizeof(beg));
memset(link,0,sizeof(link));
read(n);read(m);read(l);
for(register int i=1;i<=l;++i)
{
int u,v;read(u);read(v);
for(register int k=1;k<=3;++k)insert(u,ball(v,k));
}
for(register int i=1;i<=m;++i)insert(ball(i,1),ball(i,3));
for(register int i=1;i<=n+3*m;++i)
if(!link[i])ans+=bfs(i);
write(ans-n,'\n');
for(register int i=1;i<=n;++i)write((link[i]-n+2)/3,' ');
puts("");
}
return 0;
}

【刷题】UOJ #171 【WC2016】挑战NPC的更多相关文章

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

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

  2. [bzoj4405][wc2016]挑战NPC

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

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

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

  4. [UOJ171][WC2016]挑战NPC

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

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

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

  6. [WC2016]挑战NPC

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

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

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

  8. 刷题[极客大挑战 2019]HardSQL

    解题思路 打开是一个登陆框,考点sql注入了,先正常尝试一波 发现居然是get类型 输入and发现有waf过滤,那fuzz一波看看过滤了什么 fuzz union被过滤,并且绕过union的几种方法同 ...

  9. 「WC2016」挑战NPC

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

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

    [BZOJ4405][WC2016]挑战NPC(带花树) 题面 BZOJ 洛谷 Uoj Description 小N最近在研究NP完全问题,小O看小N研究得热火朝天,便给他出了一道这样的题目: 有n个 ...

随机推荐

  1. 封装一个CSVHelper

    public class CSVHelper { /// <summary> /// CSV转换成DataTable(OleDb数据库访问方式) /// </summary> ...

  2. ASP.NET MVC 微信JS-SDK认证

    layout: post title: ASP.NET MVC 微信JS-SDK认证 category: .net date: 2016-11-01 00:00:00 tags: .net javas ...

  3. Git学习系列 (一)

    打算花一个半月的时间学完Git.宏观上有更深的认识. 参考: Pro Git(中文版) 一.历史 本地版本控制系统 最原始的做法.复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区 ...

  4. Ubuntu16.04比较好的一系列软件安装介绍

    https://blog.csdn.net/Gerald_Jones/article/details/80784976

  5. Java开发工程师(Web方向) - 03.数据库开发 - 第4章.事务

    第4章--事务 事务原理与开发 事务Transaction: 什么是事务? 事务是并发控制的基本单位,指作为单个逻辑工作单元执行的一系列操作,且逻辑工作单元需满足ACID特性. i.e. 银行转账:开 ...

  6. chorme打开网页的技巧

    恢复之前关闭的网页 ctr l+ shift + t 打开之前不小心关闭的网页 临时书签 在设置书签中有 为打开的网页添加书签 的选项, 清除地址栏搜索记录 首先需要退出个人谷歌账户,账户上的搜索记录 ...

  7. [Clr via C#读书笔记]Cp4类型基础

    Cp4类型基础 Object类型 Object是所有类型的基类,有Equals,GetHashCode,ToString,GetType四个公共方法,其中GetHashCode,ToString可以o ...

  8. [leetcode-783-Minimum Distance Between BST Nodes]

    Given a Binary Search Tree (BST) with the root node root, return the minimum difference between the ...

  9. NFC进场通信总结概述

    简介 本文介绍Nokia设备所支持的近场通信技术(NFC)及相关的功能.旨在为使用 Qt/Symbian/Java™ API为Nokia手机开发应用的开发者 刚开始接触NFC开发时提供有用的信息. 什 ...

  10. Check the string

    A has a string consisting of some number of lowercase English letters 'a'. He gives it to his friend ...