hdu3861 强连通分量缩点+二分图最最小路径覆盖
The King’s Problem
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 3471 Accepted Submission(s):
1231
Now the king asks for your help, he wants to know the least number of states he have to divide the kingdom into.
The first line for each case contains two integers n, m(0 < n <= 5000,0 <= m <= 100000), the number of cities and roads in the kingdom. The next m lines each contains two integers u and v (1 <= u, v <= n), indicating that there is a road going from city u to city v.
题意:一个有向图,让你按规则划分区域,要求划分的区域数最少。
规则如下:1、有边u到v以及有边v到u,则u,v必须划分到同一个区域内。2、一个区域内的两点至少要有一方能到达另一方。3、一个点只能划分到一个区域内。
解题思路:根据规则1可知必然要对强连通分量进行缩点,缩点后变成了一个弱连通图。根据规则2、3可知即是要求图的最小路径覆盖。
定义:
最小路径覆盖:在图中找一些路径(路径数最少),使之覆盖了图中所有的顶点,且每个顶点有且仅和一条路径有关联。
最小顶点覆盖:在图中找一些点(顶点数最少),使之覆盖了图中所有的边,每条边至少和一个顶点有关联。
二分图:最小顶点覆盖=最大匹配数。
最小路径覆盖=顶点数-最大匹配数。
二分图最最小路径覆盖:https://www.cnblogs.com/justPassBy/p/5369930.html
匈牙利算法:https://blog.csdn.net/dark_scope/article/details/8880547
代码:
#include<stdio.h>
#include<vector>
#include<stack>
#include<string.h>
using namespace std;
vector<int> s[5050];//
stack<int> st;
int vt[5050];
int cnt,ct;
int low[5050],dfn[5050];
int bl[5050],nd[5050];//例:如果是a-->b,则bl[b]=a;如果a点再经过tarjan算法后属于第i个集合,nd[a]=i;
struct
{
int x,y;
}mp[100050];
int min(int a,int b)
{
if(a<=b)
return a;
return b;
}
int tarjan(int a)//tarjan算法
{
int i,j;
low[a]=dfn[a]=cnt++;
vt[a]=1;
st.push(a);
for(i=0;i<s[a].size();i++)
{
int u=s[a][i];
if(!dfn[u])
{
tarjan(u);
low[a]=min(low[a],low[u]);
}
else if(vt[u])
low[a]=min(low[a],dfn[u]);
}
if(low[a]==dfn[a])
{
int x;
ct++;
do//为缩点作准备
{
x=st.top();
vt[x]=0;
nd[x]=ct;
st.pop();
}while(x!=a);
}
return 0;
}
int find(int a)//匈牙利算法
{
int i,j;
for(i=0;i<s[a].size();i++)
{
int u=s[a][i];
if(!vt[u])
{
vt[u]=1;
if(bl[u]==0||find(bl[u]))
{
bl[u]=a;
//printf("www%d %d\n",bl[u],u);
return 1;
}
}
}
return 0;
}
int main()
{
int n,m,t;
int i,j;
int a,b,sum;
scanf("%d",&t);
while(t--)
{
memset(dfn,0,sizeof(dfn));
memset(vt,0,sizeof(vt));
memset(bl,0,sizeof(bl));
ct=0;
cnt=1;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
s[i].clear();
for(i=1;i<=m;i++)
{
scanf("%d%d",&mp[i].x,&mp[i].y);
s[mp[i].x].push_back(mp[i].y);
}
for(i=1;i<=n;i++)
if(!dfn[i])tarjan(i);
sum=0;
for(i=1;i<=n;i++)
s[i].clear();
for(i=1;i<=m;i++)//缩点并重新制图
{
int u,v;
u=nd[mp[i].x];
v=nd[mp[i].y];
if(u!=v)
s[u].push_back(v);
}
for(i=1;i<=ct;i++)
{
memset(vt,0,sizeof(vt));
if(find(i))
sum++;
}
printf("%d\n",ct-sum);
}
return 0;
}
例:
6 6
1 2
2 3
3 1
4 1
5 2
6 3
3
10 11
1 2
2 3
3 1
3 4
4 5
5 6
6 7
7 5
10 9
9 8
8 4
2