HDU 3861 The King’s Problem(tarjan縮點+最小路徑覆蓋:sig-最大二分匹配數,經典題)...
阿新 • • 發佈:2019-01-04
The King’s Problem
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 4080 Accepted Submission(s): 1430
Now the king asks for your help, he wants to know the least number of states he have to divide the kingdom into. Input The first line contains a single integer T, the number of test cases. And then followed T cases.
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. Output The output should contain T lines. For each test case you should just output an integer which is the least number of states the king have to divide into. Sample Input 1 3 2 1 2 1 3 Sample Output 2 Source 題目意思:
現在有n個點,m條邊的有向圖,要求劃分的區域最少
規則如下:
1.可以互相到達的點必須屬於一個區域
2.u可以到v或者v可以到v,即一個區域內任意兩點u,v,必須存在路徑從u->v或者從v->u
3.一個點只能屬於一個區域
4.所有點都應該被劃分 分析:
可以互相到達的點肯定是屬於一個強連通分量的,所以利用tarjan將屬於同一個強連通分量的點縮成一個點
得到新圖,現在新圖是一個DAG圖,有向無環圖 最小路徑的定義:在一個有向圖中,找出最少的路徑,使得這些路徑經過了所有的點 對照一下題目:一個區域其實就是一條路徑
最少的區域數目就是最少的路徑數目
所以題目轉換成最小不相交的路徑覆蓋,注意:不相交的路徑,疑問一個點只能屬於一個區域
所以對得到的新圖,求一遍最大二分匹配就好
最大二分匹配用匈牙利演算法寫
#include<stdio.h> #include<iostream> #include<math.h> #include<string.h> #include<set> #include<map> #include<list> #include<math.h> #include<queue> #include<algorithm> using namespace std; typedef long long LL; #define INF 0x7fffffff #define mem(a,x) memset(a,x,sizeof(a)) int mon1[13]= {0,31,28,31,30,31,30,31,31,30,31,30,31}; int mon2[13]= {0,31,29,31,30,31,30,31,31,30,31,30,31}; int dir[4][2]= {{0,1},{0,-1},{1,0},{-1,0}}; int getval() { int ret(0); char c; while((c=getchar())==' '||c=='\n'||c=='\r'); ret=c-'0'; while((c=getchar())!=' '&&c!='\n'&&c!='\r') ret=ret*10+c-'0'; return ret; } #define max_v 5005 int dfn[max_v]; int low[max_v]; int vis[max_v]; int stk[max_v]; int color[max_v]; vector<int> G[max_v]; vector<int> G2[max_v]; int n,m; int sig,cnt,sp; int link[max_v]; int match[max_v]; void init() { mem(dfn,0); mem(low,0); mem(vis,0); mem(stk,0); mem(color,0); for(int i=1;i<=n;i++) { G[i].clear(); G2[i].clear(); } sig=0; cnt=1; sp=-1; } int tarjan(int u) { vis[u]=1; low[u]=dfn[u]=cnt++; stk[++sp]=u; for(int j=0;j<G[u].size();j++) { int v=G[u][j]; if(vis[v]==0) tarjan(v); if(vis[v]==1) low[u]=min(low[u],low[v]); } if(low[u]==dfn[u]) { sig++; do { color[stk[sp]]=sig; vis[stk[sp]]=-1; }while(stk[sp--]!=u); } } int dfs(int u) { for(int j=0;j<G2[u].size();j++) { int v=G2[u][j]; if(vis[v]==0) { vis[v]=1; if(match[v]==-1||dfs(match[v])) { match[v]=u; return 1; } } } return 0; } int max_match()//匈牙利演算法 { mem(match,-1); int ans=0; for(int i=1;i<=sig;i++) { mem(vis,0); if(dfs(i)) ans++; } return ans; } int main() { int t; cin>>t; int x,y; while(t--) { scanf("%d %d",&n,&m); init(); for(int i=1;i<=m;i++) { scanf("%d %d",&x,&y); if(count(G[x].begin(),G[x].end(),y)==0)//重邊 G[x].push_back(y); } for(int i=1;i<=n;i++) { if(vis[i]==0) tarjan(i); } for(int i=1;i<=n;i++) { for(int j=0;j<G[i].size();j++) { if(color[i]!=color[G[i][j]]) { if(count(G2[color[i]].begin(),G2[color[i]].end(),color[G[i][j]])==0)//重邊 G2[color[i]].push_back(color[G[i][j]]); } } } printf("%d\n",sig-max_match());//最小不相交路徑覆蓋=新圖點數-最大二分匹配數 } return 0; } /* 題目意思: 現在有n個點,m條邊的有向圖,要求劃分的區域最少 規則如下: 1.可以互相到達的點必須屬於一個區域 2.u可以到v或者v可以到v,即一個區域內任意兩點u,v,必須存在路徑從u->v或者從v->u 3.一個點只能屬於一個區域 4.所有點都應該被劃分 分析: 可以互相到達的點肯定是屬於一個強連通分量的,所以利用tarjan將屬於同一個強連通分量的點縮成一個點 得到新圖,現在新圖是一個DAG圖,有向無環圖 最小路徑的定義:在一個有向圖中,找出最少的路徑,使得這些路徑經過了所有的點 對照一下題目:一個區域其實就是一條路徑 最少的區域數目就是最少的路徑數目 所以題目轉換成最小不相交的路徑覆蓋,注意:不相交的路徑,疑問一個點只能屬於一個區域 最小不相交路徑覆蓋=點數-最大二分匹配 所以對得到的新圖,求一遍最大二分匹配就好 最大二分匹配用匈牙利演算法寫 gameover! */