题目大意:给你 1~N 个点, M 条有向边。问你最少需要多少个机器人,让它们走完所有节点,不同的机器人可以走过同样的一条路,图保证为 DAG。
很明显是 最小可相交路径覆盖 问题。要先通过闭包建图后,再当作 最小不可交路径覆盖 问题 求解即可。
原因:
与 最小不可交路径覆盖 问题不同的是,两个机器人可以走相同的边,在最小覆盖的基础上如果还要走过相同的边,那么说明后一个机器人到达某一个未被走过的节点时,必须要经过某一条路,即已经走过的这条路。
比如,前一个机器人已经走了 A-->B-->C ,而后一个机器人为了到 D 点,走 A-->B-->D ,则重复的路为 A-->B 。如果我们用闭包传递后,在 A 能到达的所有节点上进行建图的话,那么 A-->B 是单独的一条, A-->C 与 A-->D 也是单独的一条,这样就使得 A 到 D 的话就不需要再经过 A-->B 了,就变成不可交的了。
同样,对于 A-->B 的点,建立 Ax-->By,变成二分图即可。(最小不可交路径覆盖问题)
代码如下:
#include<iostream> #include<algorithm> #include<string.h> #define maxn 508 using namespace std; int n,m,cnt; int head[maxn],c[maxn]; bool flag[maxn][maxn],vis[maxn]; struct Edge { int to; int next; }edge[maxn*maxn*2]; inline void add(int u,int v) { edge[++cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt; return; } void floyd() { for(int k=1;k<=n;k++){ for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ if(flag[i][k]&&flag[k][j]) flag[i][j]=true,add(i,j); } } } return; } int dfs(int u) { for(int i=head[u];i;i=edge[i].next){ int v=edge[i].to; if(vis[v]) continue; vis[v]=true; if(c[v]==0||dfs(c[v])){ c[v]=u; return 1; } } return 0; } void init() { cnt=0; for(int i=1;i<=n;i++) { head[i]=c[i]=0; for(int j=i;j<=n;j++){ flag[i][j]=flag[j][i]=false; } } return; } int main() { //freopen("test.in","r",stdin); while(~scanf("%d%d",&n,&m)){ init(); if(n==0&&m==0) break; if(m==0){printf("%d ",n );continue;} int A,B; while(m--) { scanf("%d%d",&A,&B); flag[A][B]=true; add(A,B); } floyd(); int ans=0; for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++) vis[j]=false; ans+=dfs(i); } printf("%d ",n-ans ); } }