版权声明:本文为博主原创文章,你们可以随便转载 https://blog.csdn.net/Jaihk662/article/details/83662683
链接:https://ac.nowcoder.com/acm/contest/216/E
来源:牛客网
题目描述
对于任何一个高速发展的发展中国家而言,一个高效的领导小组是不可或缺的。
现在我们知道k国的领导小组有n个人,准备举行一次会议,他们一共需要处理m个重要事项,第i个重要事项在ai手中,并且该重要事项需要交给bi来具体实施。
人都到齐后,他们会进行一个“交换意见”的环节,即每个人都会把自己手中一个自己认为关键的事项i的相关材料转发给该事项的具体实施者bi(如果该人手中没有重要事项,则不进行操作),随后,每个人都从自己收到的重要事项中选择一个自己认为关键的去实施,每实施一个事项,可以获得1点效率。
很显然,领导小组希望在这次会议中的效率更高,请你帮助他们决定在效率最高的情况下,哪些事项是必须执行的。
输入描述:
第一行两个正整数n(n<=500),m(m<=20000); 接下来m行,第i+1行两个正整数ai和bi表示重要事项i在ai手中,并且需要交给bi具体实施,可能存在ai=bi的情况
输出描述:
第一行一个正整数ans,num表示该会议的最高效率和必须执行的事项个数; 接下来num行,每行有一个正整数,表示在最高效率的情况下,必须执行的事项的标号,按照字典序从小到大输出。
输入
3 3 1 2 1 3 2 3
输出
2 2 1 3
一个人只能转手一次材料 → 二分图左边的每个点只能选一次
一个人只能实施一次计划 → 二分图右边的每个点只能选一次
一份计划经过转手+实施效率+1 → 选中一条边+1效率
所以这道题就是个二分匹配
将每个人拆成两个点,一个点表示转手(左),一个点表示实施(右), 对于第i个计划ai, bi,左边第ai个点向右边第bi个点连边
求出最大匹配就是第一问的答案,很简单
而第二问相当于是有多少条边你删掉它之后,最大匹配会-1,输出这些边
考虑直接暴力删除每一条边,每次求一遍最大匹配,复杂度O(nm²), 会超时
仔细分析一下可以发现不需要暴力每条边,只要先求一次二分匹配,然后暴力最大匹配的那些边即可,复杂度O(n²m)
除此之外二分匹配可以通过预处理增广路优化成O(nmsqrt(n)),加上其实跑不满,完全可过
#pragma comment(linker, "/STACK:102400000,102400000")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<map>
#include<string>
#include<math.h>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
#define LL long long
#define mod 1000000007
vector<int> G[505], F[505], B;
int ban, n, m, dis, lx[505], ly[505], dx[505], dy[505], vis[505], q[20005];
int HKSech();
int Sech(int u);
int main(void)
{
int u, v, i, Ans, p, ans, cnt;
scanf("%d%d", &n, &m);
for(i=1;i<=m;i++)
{
scanf("%d%d", &u, &v);
G[u].push_back(v);
F[u].push_back(i);
}
m = n;
cnt = Ans = ban = 0;
memset(lx, -1, sizeof(lx));
memset(ly, -1, sizeof(ly));
while(HKSech())
{
memset(vis, 0, sizeof(vis));
for(i=1;i<=n;i++)
{
if(lx[i]==-1 && Sech(i))
Ans++;
}
}
for(i=1;i<=n;i++)
{
if(lx[i]!=-1)
B.push_back(lx[i]);
}
for(p=0;p<B.size();p++)
{
ans = 0, ban = B[p];
memset(lx, -1, sizeof(lx));
memset(ly, -1, sizeof(ly));
while(HKSech())
{
memset(vis, 0, sizeof(vis));
for(i=1;i<=n;i++)
{
if(lx[i]==-1 && Sech(i))
ans++;
}
}
if(ans!=Ans)
q[++cnt] = ban;
}
printf("%d %d\n", Ans, cnt);
sort(q+1, q+cnt+1);
for(i=1;i<=cnt;i++)
printf("%d\n", q[i]);
return 0;
}
int HKSech()
{
int i, x, v;
dis = 100000000;
queue<int> q;
memset(dx, -1, sizeof(dx));
memset(dy, -1, sizeof(dy));
for(i=1;i<=n;i++)
{
if(lx[i]==-1)
{
q.push(i);
dx[i] = 0;
}
}
while(q.empty()==0)
{
x = q.front();
q.pop();
if(dx[x]>dis)
break;
for(i=0;i<G[x].size();i++)
{
v = G[x][i];
if(F[x][i]==ban)
continue;
if(dy[v]==-1)
{
dy[v] = dx[x]+1;
if(ly[v]==-1)
dis = dy[v];
else
{
dx[ly[v]] = dy[v]+1;
q.push(ly[v]);
}
}
}
}
if(dis==100000000)
return 0;
return 1;
}
int Sech(int x)
{
int i, v;
for(i=0;i<G[x].size();i++)
{
v = G[x][i];
if(F[x][i]==ban)
continue;
if(vis[v]==0 && dy[v]==dx[x]+1)
{
vis[v] = 1;
if(ly[v]!=-1 && dy[v]==dis)
continue;
if(ly[v]==-1 || Sech(ly[v]))
{
ly[v] = x;
lx[x] = F[x][i];
return 1;
}
}
}
return 0;
}