题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3861

题意:输入t,表示t个样例。接下来每个样例第一行有两个数n,m表示点数和有向边的数量,接下来输入m条有向边,现在要我们把点划分成最少的块,每个块里面的点两两之间要至少有一条有向边可以从其中一个点到另一个点。

思路:分成的区域里面两个点之间至少要有一个点可以到达另一个点,并且要区域数最少,那么就是求最小路径覆盖,但是要求最小路径覆盖的前提是要是无环,所以我们要先用tarjan算法缩点,然后在在缩完点的图上面建一个二分图来求最小路径覆盖。

最小路径覆盖我是刚刚接触,推荐博客:https://www.cnblogs.com/jianglangcaijin/p/5989907.html

这里我用的是链式前向星,没有用vector,代码长了一点

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<set>
#include<cstdio>
#include<string>
#include<deque>
using namespace std;
typedef long long LL;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define maxn 5005
struct node{
int v,next;
}edge1[maxn**],edge2[maxn**];
int head1[maxn],dfn[maxn],low[maxn],s[maxn],color[maxn];
int head2[maxn],pre[maxn*];
bool vis[maxn*];
int top,cnt1,time,color_num;
int cnt2,ans;
int n,m,k,t;
void init()
{
memset(head1,-,sizeof(head1));
memset(dfn,,sizeof(dfn));
memset(vis,false,sizeof(vis));
memset(pre,-,sizeof(pre));
memset(head2,-,sizeof(head2));
cnt1=time=color_num=top=;
cnt2=ans=;
}
void addedge1(int u,int v)//第一次建图添加边
{
edge1[++cnt1].v=v;
edge1[cnt1].next=head1[u];
head1[u]=cnt1;
}
void addedge2(int u,int v)//第二次建图添加边
{
edge2[++cnt2].v=v;
edge2[cnt2].next=head2[u];
head2[u]=cnt2; }
void DFS(int u)//缩点(染色)
{
low[u]=dfn[u]=++time;
vis[u]=true;
s[top++]=u;
for(int i=head1[u];i!=-;i=edge1[i].next)
{
int v=edge1[i].v;
if(!dfn[v])
{
DFS(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
color_num++;
int v;
do{
v=s[--top];
vis[v]=false;
color[v]=color_num;
}while(u!=v);
}
}
void tarjan()
{
for(int i=;i<=n;i++)
{
if(!dfn[i])
DFS(i);
}
}
void rebuild()
{
for(int i=;i<=n;i++)
{
for(int j=head1[i];j!=-;j=edge1[j].next)
{
int v=edge1[j].v;
int a=color[i];
int b=color[v];
if(a!=b)
addedge2(a,b);//建一个二分图
//最小路径覆盖就是点数减最大匹配,注意前提是图里面无环,所以要先缩点
}
}
}
bool hungry_DFS(int u)//求最大匹配
{
for(int i=head2[u];i!=-;i=edge2[i].next)
{
int v=edge2[i].v;
if(vis[v])
continue;
vis[v]=true;
if(pre[v]==-||hungry_DFS(pre[v]))
{
pre[v]=u;
return true;
}
}
return false;
}
void hungry()
{
for(int i=;i<=color_num;i++)
{
memset(vis,false,sizeof(vis));
if(hungry_DFS(i))
ans++;
}
printf("%d\n",color_num-ans);//注意这里是缩点之后的点数减最大匹配数
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
init();
int u,v;
for(int i=;i<=m;i++)
{
scanf("%d%d",&u,&v);
addedge1(u,v);//第一次建图
}
tarjan();//缩点
rebuild();//第二次建图
hungry();//求最大匹配
}
return ;
}

最新文章

  1. 懒加载插件- jquery.lazyload.js
  2. 安利eclipse插件之log4E
  3. Linux 搭建SVN 服务器(转)
  4. 聚集索引、非聚集索引、聚集索引组织表、堆组织表、Mysql/PostgreSQL对比、联合主键/自增长、InnoDB/MyISAM(引擎方面另开一篇)
  5. Kafka 集群消息监控系统:Kafka Eagle
  6. Linux笔记(三) - 文件搜素
  7. 浅谈angular中的promise
  8. dotpeek的导出
  9. R实战 第五篇:绘图(ggplot2)
  10. asp.net core 系列 9 环境(Development、Staging 、Production)
  11. linux18.04+jdk11.0.2+hadoop3.1.2部署伪分布式
  12. Spring boot 源码分析(一)SpringApplication.run(上)
  13. ORM基本操作回顾
  14. ASP.NET MVC Routing Debugger路由调试工具
  15. AIC和BIC
  16. 【PHP】php生成一个不重复的数字(订单号、会员号)
  17. LeetCode 1012 Complement of Base 10 Integer 解题报告
  18. cnn进行端到端的验证码识别改进
  19. gradle 转 maven
  20. Android开发:SharedPreferences 存储数据、获取数据

热门文章

  1. keepalived nginx 双机热备图文讲解
  2. AIR程序调用本地默认应用程序打开本地文件
  3. virtual安装linux
  4. ubuntu卸载福昕阅读器
  5. celery.backends.base.NotRegistered.
  6. python 连接 Oracle 乱码问题(cx_Oracle)
  7. C#实现联合体
  8. servlet编码问题
  9. linux 一种小的性能优化手段
  10. Spring/SpringBoot定义统一异常错误码返回