输入 #1
11 12
1 2
1 3
1 4
2 5
3 6
4 7
5 8
6 9
7 10
8 11
9 11
10 11
输出 #1
1 4 7 10 11
2 5 8
3 6 9
3
说明/提示
1≤n≤150,1≤m≤6000
网络流可求最小路径覆盖,算是模板题了
先拆点,然后建超级源超级汇,超级源连所有拆点的左边的点,然后右边的点连超级汇,对应输入的边都从左到右去连边就可以,边权都为1.
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
using namespace std;
#define inf 0x7fffffff
const int maxn = 155;
const int maxm = 60005;
struct Edge
{
int v,w,nxt;
} g[maxm];
int head[maxm];
int cnt;
void addEdge(int u,int v,int w)
{
g[cnt].v = v;
g[cnt].w = w;
g[cnt].nxt = head[u];
head[u] = cnt;
++ cnt;
}
int n,m,x,y,z;
int ans,flow;
int dis[maxm];
queue<int> q;
int S,T;
void init()
{
memset(head,-1,sizeof(head));
memset(g,0,sizeof(g));
cnt = 0;
memset(dis,-1,sizeof(dis));
while(!q.empty()) q.pop();
ans = 0;
}
int bfs()
{
memset(dis,-1,sizeof(dis));
while(!q.empty()) q.pop();
dis[S] = 0;
q.push(S);
while(!q.empty())
{
int u = q.front();
q.pop();
for(int i=head[u]; i!=-1; i=g[i].nxt)
{
int v = g[i].v;
if(dis[v]==-1 && g[i].w > 0)
{
dis[v] = dis[u] + 1;
q.push(v);
}
}
}
return dis[T]!=-1;
}
int dfs(int u,int exp)
{
if(u==T) return exp;
int flow=0,tmp= 0;
for(int i=head[u]; i!=-1; i=g[i].nxt)
{
int v = g[i].v;
if((dis[v] == (dis[u]+1)) && (g[i].w>0))
{
tmp = dfs(v,min(exp,g[i].w));
if(!tmp) continue;
exp -= tmp;
flow += tmp;
g[i].w -= tmp;
g[i^1].w += tmp;
if(!exp) break;
}
}
return flow;
}
int vis[maxn];
void print(int u, int fa)
{
if(fa != -1)
printf(" ");
printf("%d", u);
vis[u] = 1;
for(int i = head[u]; ~i; i = g[i].nxt)
{
int v = g[i].v;
if(v>n&&v<=2*n) v = v-n;
if(v == S||v == T||v==u) continue;
if(g[i].w==0&&vis[v]==0)
{
print(v,u);
break;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
init();
S = 0;
T = 2*n+1;
for(int i = 1; i <= n; i++)
{
addEdge(S,i,1);
addEdge(i,S,0);
addEdge(i+n,T,1);
addEdge(T,i+n,0);
}
for(int i=1; i<=m; ++i)
{
scanf("%d%d",&x,&y);
addEdge(x,y+n,1);
addEdge(y+n,x,0);
}
while(bfs())
{
ans += dfs(S,inf);
}
int cnt = 0;
for(int i = 1; i <= n; i++)
{
if(vis[i] == 0){
print(i,-1);
printf("\n");
cnt++;
}
}
printf("%d\n", cnt);
}