<题目链接>
题目大意:
一个有向图,让你按规则划分区域,要求划分的区域数最少。
规则如下:1.所有点只能属于一块区域;2,如果两点相互可达,则这两点必然要属于同一区域;3,区域内任意两点至少有一方能够到达另一方。
解题分析:
双连通的两点必须要属于一块区域,所以可以直接对相互连通的点进行缩点,然后再分析缩点后的图像,因为题目要求划分的区域最少,且区域内的"点"之间至少有一方能够到达另一方。仔细思考后,发现就是对缩点后的图求最小路径覆盖。区域内的"点"至少要有一方能够到达另一方,所以"点"之间连接的道路就可以看成他们之间的匹配关系。图的最小路径覆盖=总点数-最大匹配数。
1 #include <iostream> 2 #include <cstdio> 3 #include <vector> 4 #include <algorithm> 5 #include <cstring> 6 using namespace std; 7 8 #define clr(a,b) memset(a,b,sizeof(a)) 9 #define rep(i,s,t) for(int i=s;i<=t;i++) 10 #define pb push_back 11 const int N = 5e3+10; 12 int dfn[N], low[N], stk[N], belong[N], vis[N], match[N]; 13 bool instk[N]; 14 int top, scc, tot, n, m, T; 15 vector<int>vt[N],G[N]; 16 17 void init(){ 18 top=scc=tot=0; 19 clr(dfn,0);clr(low,0);clr(instk,false); 20 } 21 void Tarjan(int u){ //tarjan进行缩点 22 dfn[u]=low[u]=++tot; 23 stk[++top]=u;instk[u]=1; 24 for(int i=0; i<vt[u].size(); i++){ 25 int v=vt[u][i]; 26 if(!dfn[v]){ 27 Tarjan(v); 28 low[u]=min(low[u],low[v]); 29 }else if(instk[v]) 30 low[u]=min(low[u],dfn[v]); 31 } 32 if(low[u]==dfn[u]){ 33 scc++; 34 while(true){ 35 int v=stk[top--]; 36 instk[v]=0; 37 belong[v]=scc; 38 if(v==u)break; 39 } 40 } 41 } 42 bool dfs(int u){ 43 for(int i=0; i<G[u].size(); i++){ 44 int v=G[u][i]; 45 if(!vis[v]){ 46 vis[v]=1; 47 if(match[v]==-1||dfs(match[v])){ 48 match[v]=u; 49 return true; 50 } 51 } 52 } 53 return false; 54 } 55 int Hungary(){ //匈牙利匹配,对缩点后的"点"求最大匹配 56 int res=0; 57 clr(match,-1); 58 rep(i,1,scc){ 59 clr(vis,0); 60 if(dfs(i))res++; 61 } 62 return res; 63 } 64 int main(){ 65 scanf("%d",&T);while(T--){ 66 scanf("%d%d",&n,&m); 67 for(int i=0; i<=n; i++) vt[i].clear(); 68 rep(i,1,m){ 69 int u,v;scanf("%d%d",&u,&v); 70 vt[u].pb(v); 71 } 72 init(); 73 rep(i,1,n) 74 if(!dfn[i]) Tarjan(i); //对所有双连通的点进行缩点 75 rep(i,0,n)G[i].clear(); 76 rep(u,1,n) for(int i=0;i<vt[u].size();i++){ 77 int v=vt[u][i]; 78 if(belong[u]!=belong[v]) 79 G[belong[u]].pb(belong[v]); 80 } 81 int res=Hungary(); 82 printf("%d\n",scc-res); //求出缩点后的"点"的最小路径覆盖 83 } 84 }
2018-11-27