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. 当进行数据查询时候 要考虑创建一个model ;具备传入与输出的字段

    当进行数据查询时候 要考虑创建一个model ;具备传入与输出的字段

  2. Nginx 如何限制响应速度

    在 location 里设置 location { set $limit_rate 1k; 表示每秒只响应1k的速度 }

  3. .net core 2.0 Autofac

    参考自 https://github.com/VictorTzeng/Zxw.Framework.NetCore 安装Autofac,在`project.csproj`加入 <PackageRe ...

  4. springboot 学习

    参考:http://www.cnblogs.com/sam-uncle/p/8796212.html spring boot 系列之一:spring boot 入门 注意:main启动类和contro ...

  5. DRF 版本和认证

    Django Rest Framework 版本控制组件 DRF的版本 版本控制是做什么用的, 我们为什么要用 首先我们要知道我们的版本是干嘛用的呢~~大家都知道我们开发项目是有多个版本的~~ 当我们 ...

  6. My Brute HDU - 3315(KM || 费用流)

    题意: 有S1到Sn这n个勇士要和X1到Xn这n个勇士决斗,初始时,Si的决斗对象是Xi. 如果Si赢了Xi,那么你将获得Vi分,否则你将获得-Vi分. Si和Xi对决时,Si有初始生命Hi,初始攻击 ...

  7. power designer 一般常用快捷键(转)

    一般快捷键 快捷键 说明 F4 打开检查模型窗口,检查模型 F5 如果图窗口内的图改变过大小,恢复为原有大小即正常大小 F6 放大图窗口内的图 F7 缩小图窗口内的图 F8 在图窗口内中查看全部图内容 ...

  8. U盘启动盘还原

    cmd运行 diskpart list disk clean 一般都是disk 1,不过最好先list查一下 右击桌面上的计算机图标,选择管理,进入磁盘管理,能看到u盘分区是未分配的(黑色),右击,新 ...

  9. python学习日记(函数--装饰器)

    楔子 前提,我有一段代码(一个函数). import time def run_time(): time.sleep(0.1) print('我曾踏足山巅') 需求1:现在,我想计算这段代码的运行时间 ...

  10. ElasticSearch5.4.1 搜索引擎搭建文档

    安装配置JDK环境JDK安装(不能安装JRE)JDK下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-download ...