http://codeforces.com/problemset/problem/999/E

题意 有向图    给你n个点,m条边,以及一个初始点s,问你至少还需要增加多少条边,使得初始点s与剩下其他的所有点都连通。

第一个想法自然是通过上标记的方法,对每一个入度为0的点跑dfs。

但是问题在于剩下没有上标记的点,是成环的点。这些点不能有效的形成我们希望的拓扑序。

第一个想法是可以考虑上特殊标记,顺序枚举每个环上的点跑dfs,对每个随机跑的点上标记,在dfs的过程中如果可以经过之前枚举跑到的起点,就去掉这个点的标记,随后统计特殊标记的数量,经过测试,确实可以AC

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
const double eps = 1e-;
const int maxn = ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,tmp,K,s;
bool vis[maxn];
bool vis2[maxn];
bool vis3[maxn];
int ind[maxn];
struct Edge{
int to,next;
}edge[maxn];
int head[maxn],tot,ans;
void init(){
Mem(head,);
tot = ;
}
void add(int u,int v){
edge[++tot].next = head[u];
edge[tot].to = v;
head[u] = tot;
}
void dfs(int t){
vis[t] = ;
for(int i = head[t]; i;i = edge[i].next){
int v = edge[i].to;
if(vis[v]) continue;
dfs(v);
}
}
void dfs2(int t){
vis3[t] = ;
vis[t] = ;
for(int i = head[t];i;i = edge[i].next){
int v = edge[i].to;
if(vis2[v]){
vis2[v] = ;
ans--;
}
if(vis3[v]) continue;
dfs2(v);
}
}
int main()
{
scanf("%d%d%d",&N,&M,&s);
init();
For(i,,M){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
ind[v]++;
}
dfs(s);
ans = ;
For(i,,N){
if(!ind[i] && !vis[i]){
ans++;
dfs(i);
}
}
For(i,,N){
if(!vis[i]){
Mem(vis3,);
ans++;
dfs2(i);
vis2[i] = ;
}
}
Pri(ans);
#ifdef VSCode
system("pause");
#endif
return ;
}

第二个想法是标记覆盖,顺序dfs每一个点,以每一个点为起点经过的点用不同标记覆盖,最后判断所有标记的数量

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
const double eps = 1e-;
const int maxn = ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,tmp,K,s;
int vis[maxn];
bool vis2[maxn];
bool vis3[maxn];
int ind[maxn];
struct Edge{
int to,next;
}edge[maxn];
int head[maxn],tot,ans;
void init(){
Mem(head,);
tot = ;
}
void add(int u,int v){
edge[++tot].next = head[u];
edge[tot].to = v;
head[u] = tot;
}
void dfs(int t){
vis[t] = tmp;
for(int i = head[t]; i;i = edge[i].next){
int v = edge[i].to;
if(vis[v] == tmp) continue;
dfs(v);
}
}
int main()
{
scanf("%d%d%d",&N,&M,&s);
init();
For(i,,M){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
tmp = ;
dfs(s);
For(i,,N){
if(!vis[i]){
tmp++;
dfs(i);
}
}
ans = ;
vis2[] = ;
For(i,,N){
if(!vis2[vis[i]]){
vis2[vis[i]] = ;
ans++;
}
}
Pri(ans);
#ifdef VSCode
system("pause");
#endif
return ;
}

当然,以上两个算法都是O(n2)的算法,这题5000的数据范围可以跑,但是当数据范围扩大的时候,就需要考虑更强的算法来解决;

用Tarjan算法将原图缩点变成一个可拓扑的dag图,直接数入度为0的点即可。

#include <map>
#include <set>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
#define For(i, x, y) for(int i=x;i<=y;i++)
#define _For(i, x, y) for(int i=x;i>=y;i--)
#define Mem(f, x) memset(f,x,sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Scl(x) scanf("%lld",&x);
#define Pri(x) printf("%d\n", x)
#define Prl(x) printf("%lld\n",x);
#define CLR(u) for(int i=0;i<=N;i++)u[i].clear();
#define LL long long
#define ULL unsigned long long
#define mp make_pair
#define PII pair<int,int>
#define PIL pair<int,long long>
#define PLL pair<long long,long long>
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
const double eps = 1e-;
const int maxn = ;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + ;
int N,M,tmp,K,s;
int vis[maxn];
struct Edge{
int to,next;
}edge[maxn];
int head[maxn],tot,ans;
int Low[maxn],dfn[maxn],Stack[maxn],belong[maxn];
int index,top,scc;
bool Instack[maxn];
int ind[maxn];
int num[maxn];
void Tarjan(int u){
int v;
Low[u] = dfn[u] = ++ index;
Stack[top++] = u;
Instack[u] = true;
for(int i = head[u];i;i =edge[i].next){
int v = edge[i].to;
if(!dfn[v]){
Tarjan(v);
if(Low[u] > Low[v]) Low[u] = Low[v];
}else if(Instack[v] && Low[u] > dfn[v]){
Low[u] = dfn[v];
}
}
if(Low[u] == dfn[u]){
scc++;
do{
v = Stack[--top];
Instack[v] = false;
belong[v] = scc;
num[scc]++;
}while(v != u);
}
}
void init(){
Mem(head,);
tot = ;
}
void add(int u,int v){
edge[++tot].next = head[u];
edge[tot].to = v;
head[u] = tot;
} int main()
{
scanf("%d%d%d",&N,&M,&s);
init();
For(i,,M){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
}
index = scc = top = ;
For(i,,N) if(!dfn[i]) Tarjan(i);
int ans = ;
For(i,,N){
for(int j = head[i];j;j = edge[j].next){
int v = edge[j].to;
if(belong[i] != belong[v]){
ind[belong[v]] = ;
}
}
}
vis[belong[s]] = ;
For(i,,N){
if(!ind[belong[i]] && !vis[belong[i]]){
vis[belong[i]] = ;
ans++;
}
}
Pri(ans);
#ifdef VSCode
system("pause");
#endif
return ;
}

CodeForces999E 双dfs // 标记覆盖 // tarjan缩点的更多相关文章

  1. hihoCoder 1185 连通性·三(Tarjan缩点+暴力DFS)

    #1185 : 连通性·三 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 暑假到了!!小Hi和小Ho为了体验生活,来到了住在大草原的约翰家.今天一大早,约翰因为有事要出 ...

  2. POJ-3352 Road Construction,tarjan缩点求边双连通!

    Road Construction 本来不想做这个题,下午总结的时候发现自己花了一周的时间学连通图却连什么是边双连通不清楚,于是百度了一下相关内容,原来就是一个点到另一个至少有两条不同的路. 题意:给 ...

  3. BZOJ2199[Usaco2011 Jan]奶牛议会——2-SAT+tarjan缩点

    题目描述 由于对Farmer John的领导感到极其不悦,奶牛们退出了农场,组建了奶牛议会.议会以“每头牛 都可以获得自己想要的”为原则,建立了下面的投票系统: M只到场的奶牛 (1 <= M ...

  4. 间谍网络(tarjan缩点)

    洛谷传送门 看着这道题给人感觉就是tarjan求SCC,然而还得判断是否能控制全部间谍,这就得先从可以贿赂的点dfs一遍. 如果没有全部被标记了,就输出NO,再从没被标记的点里找最小的标号. 如果全被 ...

  5. tarjan缩点(洛谷P387)

    此题解部分借鉴于九野的博客 题目分析 给定一个 \(n\) 个点 \(m\) 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大.你只需要求出这个权值和. 允许多次经过一条边或者一个 ...

  6. 【BZOJ-1797】Mincut 最小割 最大流 + Tarjan + 缩点

    1797: [Ahoi2009]Mincut 最小割 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1685  Solved: 724[Submit] ...

  7. HDU4612+Tarjan缩点+BFS求树的直径

    tarjan+缩点+树的直径题意:给出n个点和m条边的图,存在重边,问加一条边以后,剩下的桥的数量最少为多少.先tarjan缩点,再在这棵树上求直径.加的边即是连接这条直径的两端. /* tarjan ...

  8. poj3694(tarjan缩点+lca)

    传送门:Network 题意:给你一个连通图,然后再给你n个询问,每个询问给一个点u,v表示加上u,v之后又多少个桥. 分析:方法(1219ms):用并查集缩点,把不是桥的点缩成一个点,然后全图都是桥 ...

  9. 强连通分量tarjan缩点——POJ2186 Popular Cows

    这里的Tarjan是基于DFS,用于求有向图的强联通分量. 运用了一个点dfn时间戳和low的关系巧妙地判断出一个强联通分量,从而实现一次DFS即可求出所有的强联通分量. §有向图中, u可达v不一定 ...

随机推荐

  1. 检索 COM 类工厂中 CLSID 为 {91493441-5A91-11CF-8700-00AA0060263B} 的组件失败

    Symptoms When no user is interactively logged on to the server console, if you try to start a COM+ a ...

  2. linux 目录分类与文件操作

    / 虚拟根目录 一般不会在这里存储文件 /bin 二进制目录,存放需要GNU用户级的工具 /boot 启动目录,存放启动文件 /dev 设备目录,linux在这里创建设备节点 /etc 系统配置文件目 ...

  3. vue——script内容详解

    <script> export default { name: "Home", data() { return {}; }, methods: { // 组件的方法 } ...

  4. Redis——Linux(centos7.x)下Redi和PHP Redis插件安装——【一】

    Redis 安装 下载地址:http://redis.io/download,下载最新文档版本. $ wget http://download.redis.io/releases/redis-4.0. ...

  5. Cash Machine POJ - 1276 多重背包二进制优化

    题意:多重背包模型  n种物品 每个m个  问背包容量下最多拿多少 这里要用二进制优化不然会超时 #include<iostream> #include<cstdio> #in ...

  6. 洛谷2754 [CTSC1999]家园

    题目链接:[CTSC1999]家园 这个题目我们不是很好在做网络流的时候判断是否有解,因此我们考虑分开来做 对于是否有解的判断,我们唯一需要解决的是飞船的周期停泊问题,对于这个问题,我们可以用并查集解 ...

  7. 【XSY2691】中关村 卢卡斯定理 数位DP

    题目描述 在一个\(k\)维空间中,每个整点被黑白染色.对于一个坐标为\((x_1,x_2,\ldots,x_k)\)的点,他的颜色我们通过如下方式计算: 如果存在一维坐标是\(0\),则颜色是黑色. ...

  8. 【AGC018F】Two Trees 构造 黑白染色

    题目描述 有两棵有根树,顶点的编号都是\(1\)~\(n\). 你要给每个点一个权值\(a_i\),使得对于两棵树的所有顶点\(x\),满足\(|x\)的子树的权值和\(|=1\) \(n\leq 1 ...

  9. day5 模拟用户登录

    _user= "yangtuo" _passwd = " # passd_authentication = False #flag 标志位 for i in range( ...

  10. 洛谷P4891 序列(势能线段树)

    洛谷题目传送门 闲话 考场上一眼看出这是个毒瘤线段树准备杠题,发现实在太难调了,被各路神犇虐哭qwq 考后看到各种优雅的暴力AC......宝宝心里苦qwq 思路分析 题面里面是一堆乱七八糟的限制和性 ...