HDU #1232 : 畅通工程
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1232
题意:给出n个点m条边,问至少还需要添加多少条边才能使整个图连通。
思路:对没有访问过的点进行dfs,每次dfs中的一团为一个连通块,答案即为“连通块数量-1”。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<utility>
#include<algorithm>
#include<utility>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<ctime>
#include<functional>
#include<bitset>
#define P pair<int,int>
#define ll long long
#define ull unsigned long long
#define lson id*2,l,mid
#define rson id*2+1,mid+1,r
#define ls id*2
#define rs (id*2+1)
#define Mod(a,b) a<b?a:a%b+b
#define cl0(a) memset(a,0,sizeof(a))
#define cl1(a) memset(a,-1,sizeof(a))
using namespace std;
const ll M = 1e9 + 7;
const ll INF = 2e9 + 10;
const int N = 410;
const double _e = 10e-6;
const int maxn = 1e3 + 10;
const int matSize = 9;
const int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 };
const int _dx[8] = { -1,-1,-1,0,0,1,1,1 }, _dy[8] = { -1,0,1,-1,1,-1,0,1 };
int x, y;
int n, m;
bool used[maxn][maxn], vis[maxn];
vector<int> G[maxn];
void dfs(int u)
{
vis[u] = true;
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (!vis[v])
dfs(v);
}
}
int main()
{
while (~scanf("%d", &n)) {
if (n == 0)break;
cl0(used); cl0(vis);
for (int i = 1; i <= 1000; i++)
G[i].clear();
scanf("%d", &m);
while (m--) {
scanf("%d%d", &x, &y);
if (!used[x][y]) {
G[x].push_back(y);
G[y].push_back(x);
used[x][y] = 1; used[y][x] = 1;
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
dfs(i);
ans++;
}
}
printf("%d\n", ans - 1);
}
return 0;
}
hihoCoder #1174 : 拓扑排序·一
传送门:https://hihocoder.com/problemset/problem/1174
题意:有n节课m个关系,对于每个关系a b表示a是b的前置课程,问课程安排有没有问题,也就是有没有环。
思路:记录每个点的入度,入度为0的放入队列。每次从队列取出点时将该点所有后置课程的入度-1,若后置课程入度为0则放入队列。当队列为空时结束,判断这时是否还有点的入度>0,如果有则有问题。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<utility>
#include<algorithm>
#include<utility>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<ctime>
#include<functional>
#include<bitset>
#define P pair<int,int>
#define ll long long
#define ull unsigned long long
#define lson id*2,l,mid
#define rson id*2+1,mid+1,r
#define ls id*2
#define rs (id*2+1)
#define Mod(a,b) a<b?a:a%b+b
#define cl0(a) memset(a,0,sizeof(a))
#define cl1(a) memset(a,-1,sizeof(a))
using namespace std;
const ll M = 1e9 + 7;
const ll INF = 2e9 + 10;
const int N = 410;
const double _e = 10e-6;
const int maxn = 1e5 + 10;
const int matSize = 9;
const int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 };
const int _dx[8] = { -1,-1,-1,0,0,1,1,1 }, _dy[8] = { -1,0,1,-1,1,-1,0,1 };
int x, y;
int n, m;
int in[maxn];
vector<int> G[maxn];
int main()
{
int t;
scanf("%d", &t);
while (t--) {
cl0(in);
for (int i = 1; i <= 100000; i++)
G[i].clear();
scanf("%d%d", &n, &m);
while (m--) {
scanf("%d%d", &x, &y);
G[x].push_back(y);
in[y]++;
}
queue<int> que;
while (!que.empty())que.pop();
for (int i = 1; i <= n; i++)
if (in[i] == 0)que.push(i);
while (!que.empty()) {
int u = que.front(); que.pop();
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
in[v]--;
if (in[v] == 0)
que.push(v);
}
}
bool judge = true;
for (int i = 1; i <= n; i++) {
if (in[i] > 0) {
judge = false;
break;
}
}
if (judge)
puts("Correct");
else
puts("Wrong");
}
return 0;
}
POJ #3249 : Test for Job
传送门:http://poj.org/problem?id=3249
题意:有n个点m条边,每个点有一个权值,问从一个入度为0的点沿边走到出度为0的点,最大的权值和是多少。
思路:拓扑同前一道题。维护点的最大权值和,sum[v]=max(sum[v],sum[u]+c[v])。当点的出度为0时,ans=max(ans,sum[u])。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<utility>
#include<algorithm>
#include<utility>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<ctime>
#include<functional>
#include<bitset>
#define P pair<int,int>
#define ll long long
#define ull unsigned long long
#define lson id*2,l,mid
#define rson id*2+1,mid+1,r
#define ls id*2
#define rs (id*2+1)
#define Mod(a,b) a<b?a:a%b+b
#define cl0(a) memset(a,0,sizeof(a))
#define cl1(a) memset(a,-1,sizeof(a))
using namespace std;
const ll M = 1e9 + 7;
const ll INF = 2e9 + 10;
const int N = 410;
const double _e = 10e-6;
const int maxn = 1e5 + 10;
const int matSize = 9;
const int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 };
const int _dx[8] = { -1,-1,-1,0,0,1,1,1 }, _dy[8] = { -1,0,1,-1,1,-1,0,1 };
int x, y;
int n, m;
vector<int> G[maxn];
int c[maxn];
ll ans[maxn];
int in[maxn];
int main()
{
while (~scanf("%d%d", &n, &m)) {
cl0(in);
for (int i = 1; i <= 100000; i++) {
ans[i] = -INF;
G[i].clear();
}
for (int i = 1; i <= n; i++)
scanf("%d", &c[i]);
while (m--) {
scanf("%d%d", &x, &y);
G[x].push_back(y);
in[y]++;
}
queue<int> que;
for (int i = 1; i <= n; i++) {
if (in[i] == 0) {
que.push(i);
ans[i] = c[i];
}
}
ll anss = -INF;
while (!que.empty()) {
int u = que.front(); que.pop();
if (G[u].size() == 0)
anss = max(anss, ans[u]);
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
in[v]--;
ans[v] = max(ans[v], ans[u] + c[v]);
if (in[v] == 0)
que.push(v);
}
}
printf("%lld\n", anss);
}
return 0;
}
HDU #1269 : 迷宫城堡
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=1269
题意:求SCC(强连通分量)的个数是否为1。
思路:两种方法
第一种:直接tarjan求强连通分量。tarjan算法可以参考这篇:https://blog.csdn.net/qq_34374664/article/details/77488976
第二种:一共进行2次dfs。①在逆图上求拓扑序记录到栈(dfs,在点退出时入栈);②将点从栈按顺序取出,若没访问过则进行dfs并且这一团为一个强连通分量。
AC代码:
//第一种:tarjan
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<utility>
#include<algorithm>
#include<utility>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<ctime>
#include<functional>
#include<bitset>
#define P pair<int,int>
#define ll long long
#define ull unsigned long long
#define lson id*2,l,mid
#define rson id*2+1,mid+1,r
#define ls id*2
#define rs (id*2+1)
#define Mod(a,b) a<b?a:a%b+b
#define cl0(a) memset(a,0,sizeof(a))
#define cl1(a) memset(a,-1,sizeof(a))
using namespace std;
const ll M = 1e9 + 7;
const ll INF = 1e9;
const int N = 410;
const double _e = 10e-6;
const int maxn = 1e4 + 10;
const int matSize = 9;
const int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 };
const int _dx[8] = { -1,-1,-1,0,0,1,1,1 }, _dy[8] = { -1,0,1,-1,1,-1,0,1 };
int x, y;
int n, m;
vector<int> G[maxn];
int no_sc, tot; int dfn[maxn], low[maxn], scc[maxn];
stack <int> s;
void tarjan(int u)
{
dfn[u] = low[u] = ++tot;
s.push(u);
for (int i : G[u]) {
if (dfn[i] == 0) {
tarjan(i);
low[u] = min(low[u], low[i]);
}
else if (scc[i] == 0)
low[u] = min(low[u], dfn[i]);
}
if (dfn[u] == low[u]) {
no_sc++;
while (1) {
int x = s.top(); s.pop();
scc[x] = no_sc;
if (x == u) break;
}
}
}
int main()
{
while (~scanf("%d%d", &n, &m)) {
if (n == 0 && m == 0)break;
no_sc = 0;
cl0(dfn); cl0(low); cl0(scc);
for (int i = 1; i <= 10000; i++)
G[i].clear();
while (m--) {
scanf("%d%d", &x, &y);
G[x].push_back(y);
}
for (int i = 1; i <= n; i++)
if (dfn[i] == 0)
tarjan(i);
if (no_sc == 1)
puts("Yes");
else
puts("No");
}
return 0;
}
//第二种:2次dfs
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<utility>
#include<algorithm>
#include<utility>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<ctime>
#include<functional>
#include<bitset>
#define P pair<int,int>
#define ll long long
#define ull unsigned long long
#define lson id*2,l,mid
#define rson id*2+1,mid+1,r
#define ls id*2
#define rs (id*2+1)
#define Mod(a,b) a<b?a:a%b+b
#define cl0(a) memset(a,0,sizeof(a))
#define cl1(a) memset(a,-1,sizeof(a))
using namespace std;
const ll M = 1e9 + 7;
const ll INF = 1e9;
const int N = 410;
const double _e = 10e-6;
const int maxn = 1e4 + 10;
const int matSize = 9;
const int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 };
const int _dx[8] = { -1,-1,-1,0,0,1,1,1 }, _dy[8] = { -1,0,1,-1,1,-1,0,1 };
int x, y;
int n, m;
vector<int> G[maxn], H[maxn];
stack <int> s;
bool used[maxn];
void topsort(int u)
{
used[u] = true;
for (int i = 0; i < H[u].size(); i++) {
int v = H[u][i];
if(!used[v])
topsort(v);
}
s.push(u);
}
void dfs(int u)
{
used[u] = true;
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (!used[v])
dfs(v);
}
}
int main()
{
while (~scanf("%d%d", &n, &m)) {
if (n == 0 && m == 0)break;
for (int i = 1; i <= 10000; i++) {
G[i].clear();
H[i].clear();
}
while (!s.empty())s.pop();
while (m--) {
scanf("%d%d", &x, &y);
G[x].push_back(y);
H[y].push_back(x);
}
cl0(used);
for (int i = 1; i <= n; i++) {
if (!used[i])topsort(i);
}
cl0(used);
int cnt = 0;
while (!s.empty()) {
int u = s.top(); s.pop();
if (!used[u]) {
dfs(u);
cnt++;
}
}
if (cnt == 1)
puts("Yes");
else
puts("No");
}
return 0;
}
POJ #2762 : Going from u to v or from v to u?
传送门:http://poj.org/problem?id=2762
题意:给一个n个点m条边的有向图。问是否能满足对于任意两点u v,使得从u能到v或从v能到u。
思路:先求强连通分量,以强连通分量为点建新图,新图是个DAG(有向无环图)。
由于每一块强连通分量内部是可以两两到达的,因此只要考虑新图上的点是否能从u到v或从v到u。
若要满足条件,新图必须为一条链。这里是利用拓扑来求的,当队列里同时出现>1个点时就为“No”。最后拓扑完后判断是否每个点都用过了,如果有点没用过的话也为“No”。
AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<utility>
#include<algorithm>
#include<utility>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<ctime>
#include<functional>
#include<bitset>
#define P pair<int,int>
#define ll long long
#define ull unsigned long long
#define lson id*2,l,mid
#define rson id*2+1,mid+1,r
#define ls id*2
#define rs (id*2+1)
#define Mod(a,b) a<b?a:a%b+b
#define cl0(a) memset(a,0,sizeof(a))
#define cl1(a) memset(a,-1,sizeof(a))
using namespace std;
const ll M = 1e9 + 7;
const ll INF = 2e9 + 10;
const int N = 410;
const double _e = 10e-6;
const int maxn = 1e3 + 10;
const int matSize = 9;
const int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 };
const int _dx[8] = { -1,-1,-1,0,0,1,1,1 }, _dy[8] = { -1,0,1,-1,1,-1,0,1 };
int x, y;
int n, m;
vector<int> G[maxn], H[maxn];
int in[maxn];
bool used[maxn][maxn], vis[maxn];
int no_sc, tot; int dfn[maxn], low[maxn], scc[maxn];
stack <int> s;
void tarjan(int u)
{
dfn[u] = low[u] = ++tot;
s.push(u);
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (dfn[v] == 0) {
tarjan(v);
low[u] = min(low[u], low[v]);
}
else if (scc[v] == 0)
low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
no_sc++;
while (1) {
int x = s.top(); s.pop();
scc[x] = no_sc;
if (x == u) break;
}
}
}
int main()
{
int t;
scanf("%d", &t);
while (t--) {
scanf("%d%d", &n, &m);
no_sc = 0;
cl0(dfn); cl0(low); cl0(scc);
cl0(used); cl0(vis); cl0(in);
for (int i = 1; i <= 1000; i++) {
G[i].clear();
H[i].clear();
}
while (m--) {
scanf("%d%d", &x, &y);
G[x].push_back(y);
}
for (int i = 1; i <= n; i++)
if (dfn[i] == 0)
tarjan(i);
for (int i = 1; i <= n; i++) {
for (int j = 0; j < G[i].size(); j++) {
int v = G[i][j];
if (scc[i] != scc[v]&&!used[scc[i]][scc[v]]) {
used[scc[i]][scc[v]] = true;
H[scc[i]].push_back(scc[v]);
in[scc[v]]++;
}
}
}
if (no_sc == 1) {
puts("Yes");
continue;
}
queue<int> que;
while (!que.empty())
que.pop();
for (int i = 1; i <= no_sc; i++) {
if (in[i] == 0) {
que.push(i);
vis[i] = true;
}
}
if (que.size() > 1) {
puts("No");
continue;
}
bool judge = true;
while (!que.empty()) {
int u = que.front(); que.pop();
for (int i = 0; i < H[u].size(); i++) {
int v = H[u][i];
in[v]--;
if (in[v] == 0) {
que.push(v);
vis[v] = true;
}
}
if (que.size() > 1) {
puts("No");
judge = false;
break;
}
}
if (judge) {
for (int i = 1; i <= no_sc; i++) {
if (!vis[i]) {
judge = false;
break;
}
}
if (judge)
puts("Yes");
else
puts("No");
}
}
return 0;
}