题目描述
滑雪场坐落在FJ省西北部的若干座山上。
从空中鸟瞰,滑雪场可以看作一个有向无环图,每条弧代表一个斜坡(即雪道),弧的方向代表斜坡下降的方向。
你的团队负责每周定时清理雪道。你们拥有一架直升飞机,每次飞行可以从总部带一个人降落到滑雪场的某个地点,然后再飞回总部。从降落的地点出发,这个人可以顺着斜坡向下滑行,并清理他所经过的雪道。
由于每次飞行的耗费是固定的,为了最小化耗费,你想知道如何用最少的飞行次数才能完成清理雪道的任务。
输入输出格式
输入格式:
输入文件的第一行包含一个整数n (2 <= n <= 100) – 代表滑雪场的地点的数量。
接下来的n行,描述1~n号地点出发的斜坡,第i行的第一个数为mi (0 <= mi < n) ,后面共有mi个整数,由空格隔开,每个整数aij互不相同,代表从地点i下降到地点aij的斜坡。每个地点至少有一个斜坡与之相连。
输出格式:
输出文件的第一行是一个整数k – 直升飞机的最少飞行次数。
输入输出样例
输入样例#1:
8
1 3
1 7
2 4 5
1 8
1 8
0
2 6 5
0
输出样例#1:
4
分析:
题意是寻找一个DAG的最小边覆盖。
我们首先考虑把边看做点,跑最小点覆盖。因为边很多(可能有
n
2
n^2
n2条),点可以重复走,即使网络流不T,跑传递闭包也会T。复杂度为
O
(
m
3
)
O(m^3)
O(m3)。
所以我们考虑直接对原图跑网络流。
建立一个
S
S
S向每个点连
i
n
f
inf
inf流量的边(表示可以从任意一点开始),每个点向
T
T
T连
i
n
f
inf
inf流量的边(表示可以从任意一点结束)。原图的每条边要至少走一遍,也就是流量为
[
1
,
i
n
f
)
[1,inf)
[1,inf)。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
const int maxn=207;
const int maxe=maxn*maxn*5;
const int inf=0x3f3f3f3f;
using namespace std;
int n,tot,x,cnt,s,t,sum,ans;
int ls[maxn],deg[maxn],dis[maxn];
struct edge{
int y,w,op,next;
}g[maxe];
queue <int> q;
void add(int x,int y,int w)
{
g[++cnt]=(edge){y,w,cnt+1,ls[x]};
ls[x]=cnt;
g[++cnt]=(edge){x,0,cnt-1,ls[y]};
ls[y]=cnt;
}
bool bfs()
{
memset(dis,inf,sizeof(dis));
q.push(s);
dis[s]=0;
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if ((g[i].w) && (dis[y]>dis[x]+1))
{
dis[y]=dis[x]+1;
q.push(y);
}
}
}
return (dis[t]!=inf);
}
int dfs(int x,int maxf)
{
if ((x==t) || (!maxf)) return maxf;
int ret=0;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if ((g[i].w) && (dis[y]==dis[x]+1))
{
int f=dfs(y,min(maxf-ret,g[i].w));
if (!f) dis[y]=-1;
ret+=f;
g[i].w-=f;
g[g[i].op].w+=f;
}
}
return ret;
}
int dinic()
{
int flow=0;
while (bfs())
flow+=dfs(s,inf);
return flow;
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&tot);
for (int j=1;j<=tot;j++)
{
scanf("%d",&x);
add(i,x,inf-1);
deg[i]--;
deg[x]++;
}
}
int S=n+1,T=n+2,SS=n+3,TT=n+4;
for (int i=1;i<=n;i++)
{
add(S,i,inf);
add(i,T,inf);
}
int nowcnt=cnt;
add(T,S,inf);
for (int i=1;i<=n;i++)
{
if (deg[i]>0) add(SS,i,deg[i]),sum+=deg[i];
else if (deg[i]<0) add(i,TT,-deg[i]);
}
s=SS,t=TT;
dinic();
ans=g[nowcnt+2].w;
for (int i=nowcnt+1;i<=cnt;i++) g[i].w=0;
s=T,t=S;
ans-=dinic();
printf("%d",ans);
}