HDU 3861 The King’s Problem (强连通缩点+DAG最小路径覆盖)

<题目链接>

题目大意:

一个有向图,让你按规则划分区域,要求划分的区域数最少。

规则如下: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

转载于:https://www.cnblogs.com/00isok/p/10029254.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值