思路来源
https://ac.nowcoder.com/acm/contest/view-submission?submissionId=36917493
题意
可以考虑成发放者到接受者的二分图,把一个人拆成发放者和接受者两个点。
那么最后的答案就是二分图的最大匹配。
最后要输出的必要边节点,就是删掉这条边之后会导致最大匹配减小的边。
用矩阵暴力枚举删边又加回来的过程,复杂度似乎是o(n³*m)啊。
心得
这题数据比较弱,枚举删边o(m)*邻接矩阵o(n³)卡过了。
本来思路来源700多ms,我交了一发结果800多ms...
难以想象,暴力假题……
网上有4ms的dinic+tarjan看不懂,
知道最大流却不知道为啥要用强连通分量枚举删边,回头再补。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <functional>
const int INF=0x3f3f3f3f;
const int maxn=1e5+10;
const int mod=1e9+7;
const int MOD=998244353;
const double eps=1e-7;
typedef long long ll;
#define vi vector<int>
#define si set<int>
#define pii pair<double,int>
#define pi acos(-1.0)
#define pb push_back
#define mp make_pair
#define lowbit(x) (x&(-x))
#define sci(x) scanf("%d",&(x))
#define scll(x) scanf("%lld",&(x))
#define sclf(x) scanf("%lf",&(x))
#define pri(x) printf("%d",(x))
#define rep(i,j,k) for(int i=j;i<=k;++i)
#define per(i,j,k) for(int i=j;i>=k;--i)
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
int x[20005],y[20005];
int ans[20005],tmp;
int M[505][505],cnt,n,m,cx[505],cy[505];
bool vis[505];
bool dfs(int u)
{
for(int v=0;v<n;++v)
{
if(!M[u][v])continue;
if(!vis[v])
{
vis[v]=1;
if(cy[v]==-1||dfs(cy[v]))
{
cx[u]=v;
cy[v]=u;
return 1;
}
}
}
return 0;
}
int hungary()
{
int res=0;
mem(vis,0);
mem(cx,-1);
mem(cy,-1);
rep(u,0,n-1)
{
mem(vis,0);
res+=dfs(u);
}
return res;
}
int main()
{
sci(n),sci(m);
rep(i,0,m-1)
{
sci(x[i]),sci(y[i]);
x[i]--,y[i]--;
M[x[i]][y[i]]++;
}
tmp=hungary();
rep(i,0,m-1)
{
M[x[i]][y[i]]--;//枚举删边
int p=hungary();
if(p<tmp)ans[cnt++]=i+1;
M[x[i]][y[i]]++;
}
printf("%d %d\n",tmp,cnt);
rep(i,0,cnt-1)printf("%d\n",ans[i]);
return 0;
}