一直想找个时间整理一下自己常用的模板,方便自己查找。
图论还有很多算法,后期待完善。
最小生成树
kruskal
- hdu1233
也可以用贪心的方法,先定义一个数组,排序后并查集。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using namespace std;
const int maxn = 20005;
struct Node
{
int x,y,val;
Node(){}
Node(int a,int b,int c):x(a),y(b),val(c){}
bool operator<(const Node& a)const
{
return val > a.val;
}
};
int B[maxn];
int Find(int n)
{
return n == B[n] ? n:B[n]=Find(B[n]);
}
int main()
{
// ios::sync_with_stdio(false);
// cin.tie(0);
int N = 0;
while(scanf("%d",&N)&&N){
priority_queue<Node> Q;
int a,b,c;
int T = N*(N-1)/2;
for(int i=1;i<=T;i++){
B[i] = i;
}
for(int i=1;i<=T;i++){
scanf("%d%d%d",&a,&b,&c);
Q.push(Node(a,b,c));
}
int cnt = 0;
int ans = 0;
while(!Q.empty()&&cnt<N-1){
Node t = Q.top();Q.pop();
int t1 = Find(t.x);
int t2 = Find(t.y);
if(t1!=t2){
B[t2] = t1;
ans += t.val;
cnt++;
}
}
printf("%d\n",ans);
}
return 0;
}
prim算法
大体上和Dijksra算法相似
- poj1258
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
using namespace std;
const int maxn = 105;
typedef pair<int,int> pii;
int G[maxn][maxn];
int dis[maxn];
int N = 0;
bool vis[maxn];
int prim()
{
int ans = 0;
memset(dis,0x7f,sizeof(dis));
memset(vis,0,sizeof(vis));
priority_queue<pii,vector<pii>,greater<pii> > Q;
Q.push(pii(0,0));
dis[0] = 0;
while(!Q.empty()){
pii t = Q.top();
Q.pop();
int u = t.first;
int v = t.second;
if(vis[v]) continue;
vis[v] = 1;
ans += u;
for(int i=0;i<N;i++){
if(i!=v){
int d = G[v][i];
if(dis[i] > d&&!vis[i]){ //唯一和dijkstra算法不一样的地方
dis[i] = d; //
Q.push(pii(dis[i],i));
}
}
}
}
return ans;
}
int main(int argc, char const *argv[])
{
ios::sync_with_stdio(false);
cin.tie(0);
while(cin >> N){
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
cin >> G[i][j];
}
}
cout << prim() << endl;
}
return 0;
}
最短路
dijkstra算法
- poj2387
用vecotr邻接表来表示图,适用于稀疏图
优先队列版本的dijkstra算法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
using namespace std;
const int maxn = 10005;
int T,N;
vector<int> G[maxn];
vector<int> D[maxn];
int dis[maxn];
const int inf = 1<<31;
typedef pair<int,int> pii;
int dijkstra()
{
memset(dis,10,sizeof(dis));
bool vis[maxn];
memset(vis,0,sizeof(vis));
priority_queue<pii,vector<pii>,greater<pii> > Q;
Q.push(pii(0,1));
while(!Q.empty()){
pii t = Q.top();
Q.pop();
int d = t.first;
int a = t.second;
if(vis[a])continue;
vis[a] = 1;
for(int i=0;i<G[a].size();i++){
int j = G[a][i];
int di = D[a][i];
if(dis[j] > di + d){
dis[j] = di + d;
Q.push(pii(dis[j],j));
}
}
}
return dis[N];
}
int main()
{
cin >> T >> N;
int a,b,c;
for(int i=0;i<T;i++){
cin >> a >> b >> c;
G[a].push_back(b);
G[b].push_back(a);
D[a].push_back(c);
D[b].push_back(c);
}
cout << dijkstra() << endl;
return 0;
}
bellman_ford算法
- poj3259
前M条边是双向边,后W条边是单向边,然后直接用bellman_ford算法判负环(自己用spfa判负环 结果WA了,不知道是测试数据卡spfa还是我spfa写错了)。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using namespace std;
const int maxn = 1005;
typedef pair<int,int> pii;
int dis[maxn];
int N,M,W;
int k=0;
struct Edge
{
int to,from,cost;
};
Edge E[5000];
bool bellman_Ford()
{
memset(dis,0,sizeof(dis));
for(int i=1;i<=N;i++){
for(int j=0;j<k;j++){
Edge e = E[j];
if(dis[e.to] > dis[e.from] + e.cost){
dis[e.to] = dis[e.from] + e.cost;
if(i==N) return false;
}
}
}
return true;
}
int main(int argc, char const *argv[])
{
int T = 0;
cin >> T;
while(T--){
cin >> N >> M >> W;
k = 0;
int a,b,c;
for(int i=0;i<M;i++){
cin >> E[k].from >> E[k].to >> E[k].cost;
E[k+1].from = E[k].to;
E[k+1].to = E[k].from;
E[k+1].cost = E[k].cost;
k += 2;
}
for(int i=0;i<W;i++){
cin >> E[k].from >> E[k].to >> E[k].cost;
E[k].cost = -E[k].cost;
k++;
}
bool ok = bellman_Ford();
if(ok){
cout << "NO" << endl;
}else{
cout << "YES" << endl;
}
}
return 0;
}
SPFA算法
1 |
|
Flody算法
1 | for (int i = 0; i < n; i++) { // 初始化为0 |
网络流
增广路算法
- 计蒜课排涝
模板题
EdmondsKarp也可用于二分图的最大基数匹配,在二分图的左边后右边都添加一个结点,然后分别和二分图两边的结点相连即可。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
using namespace std;
const int maxn = 205;
const int INF = 1<<30;
struct Edge
{
int from,to,cap,flow;
Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}
};
struct Dinic //更快
{
int n,m,s,t;
vector<Edge> edges;
vector<int> G[maxn];
bool vis[maxn];
int d[maxn];
int cur[maxn];
void init(int n){
for(int i=0;i<n;i++) G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int cap){
edges.push_back(Edge(from,to,cap,0));
edges.push_back(Edge(to,from,0,0));
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BFS()
{
memset(vis,0,sizeof(vis));
queue<int> Q;
Q.push(s);
d[s] = 0;
vis[s] = 1;
while(!Q.empty()){
int x = Q.front();
Q.pop();
for(int i=0;i<G[x].size();i++){
Edge& e = edges[G[x][i]];
if(!vis[e.to]&&e.cap > e.flow){
vis[e.to] = 1;
d[e.to] = d[x]+1;
Q.push(e.to);
}
}
}
return vis[t];
}
int DFS(int x,int a){
if(x==t || a==0)return a;
int flow = 0,f;
for(int &i=cur[x];i<G[x].size();i++){
Edge& e = edges[G[x][i]];
if(d[x]+1==d[e.to] && (f=DFS(e.to,min(a,e.cap-e.flow)))>0){
e.flow += f;
edges[G[x][i]^1].flow -= f;
flow += f;
a -= f;
if(a==0)break;
}
}
return flow;
}
int Maxflow(int s,int t){
this->s = s,this->t = t;
int flow = 0;
while(BFS()){
memset(cur,0,sizeof(cur));
flow += DFS(s,INF);
}
return flow;
}
};
struct EdmondsKarp
{
int n,m;
vector<Edge> edges;
vector<int> G[maxn];
int a[maxn];
int p[maxn];
void init(int n){
for(int i=0;i<n;i++) G[i].clear();
edges.clear();
}
void AddEdge(int from,int to,int cap){
edges.push_back(Edge(from,to,cap,0));
edges.push_back(Edge(to,from,0,0));
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
int Maxflow(int s,int t){
int flow = 0;
while(1){
memset(a,0,sizeof(a));
queue<int> Q;
Q.push(s);
a[s] = INF;
while(!Q.empty()){
int x = Q.front();Q.pop();
for(int i=0;i<G[x].size();i++){
Edge& e = edges[G[x][i]];
if(!a[e.to]&&e.cap>e.flow){
p[e.to] = G[x][i];
a[e.to] = min(a[x],e.cap-e.flow);
Q.push(e.to);
}
}
if(a[t]) break;
}
if(!a[t])break;
for(int u=t;u!=s;u=edges[p[u]].from){
edges[p[u]].flow += a[t];
edges[p[u]^1].flow -= a[t];
}
flow += a[t];
}
return flow;
}
};
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n,m;
cin >> n >> m;
EdmondsKarp s;
int a,b,c;
for(int i=0;i<n;i++){
cin >> a >> b >>c;
s.AddEdge(a,b,c);
}
cout << s.Maxflow(1,m) <<endl;
}
最小费用最大流
1 |
|
二分图
二分图判断(交叉染色)
1 | static int speed_up = []() { |
无向图割边割点和双连通分量
- uva315
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
using namespace std;
typedef pair<int, int> pii;
const int MAX = 5100;
int n, m, deep;
vector<int> path[MAX];
int vis[MAX], low[MAX];
vector<int> cutpoint; //割点
vector<pii> bridge; //割边,桥
int nbcc; //双连通分量数
stack<pii> order;
vector<int> bcc[MAX]; //双连通分量
void dfs(int pos, int father)
{
int i, j, total = 0;
bool cut = false;
int reback = 0; //处理平行边
vis[pos] = low[pos] = deep++;
int ls = path[pos].size();
for (j = 0; j < ls; j++)
{
i = path[pos][j];
if (i == father)
reback++;
if (vis[i] == 0)
{
pii e(pos, i);
order.push(e);
dfs(i, pos);
if (low[i] >= vis[pos])
{
nbcc++;
bcc[nbcc].clear();
pii r;
do
{
r = order.top();
order.pop();
bcc[nbcc].push_back(r.second);
} while (e != r);
bcc[nbcc].push_back(r.first);
}
total++;
low[pos] = min(low[i], low[pos]);
if ((vis[pos] == 1 && total > 1) ||
(vis[pos] != 1 && low[i] >= vis[pos]))
cut = true;
if (low[i] > vis[pos])
bridge.push_back(e);
}
else if (i != father)
{
low[pos] = min(vis[i], low[pos]);
}
}
if (reback > 1)
low[pos] = min(low[pos], vis[father]);
if (cut)
cutpoint.push_back(pos);
}
void find_cut()
{
int i;
mclear(vis);
mclear(low);
cutpoint.clear();
bridge.clear();
nbcc = 0;
while (!order.empty())
order.pop();
for (i = 1; i <= n; i++)
{
if (vis[i] == 0)
{
deep = 1;
dfs(i, -1);
}
}
}
int main()
{
while (~scanf("%d", &n) && n)
{
int a = 0;
char c;
vector<int> vet;
for (int i = 0; i <= n; i++)
{
path[i].clear();
}
int u = 0;
while (scanf("%d", &u) && u)
{
while (scanf("%c", &c)&&c==' ')
{
scanf("%d",&a);
path[u].push_back(a);
path[a].push_back(u);
}
}
find_cut();
cout << cutpoint.size() << endl;
}
return 0;
}
强连通分量
SCC的Tarjan算法
- poj1236
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
using namespace std;
const int maxn = 105;
vector<int> G[maxn];
bool M[maxn][maxn];
int pre[maxn],lowlink[maxn],sccno[maxn],dfs_clock,scc_cnt;
int out[maxn],in[maxn];
stack<int> S;
int dfs(int u)
{
pre[u] = lowlink[u] = ++dfs_clock;
S.push(u);
for(int i=0;i<G[u].size();i++){
int v = G[u][i];
if(!pre[v]){
dfs(v);
lowlink[u] = min(lowlink[u],lowlink[v]);
}else if(!sccno[v]){
lowlink[u] = min(lowlink[u],pre[v]);
}
}
if(lowlink[u] == pre[u]){
scc_cnt++;
while(1){
int x = S.top();S.pop();
sccno[x] = scc_cnt;
if(x==u)break;
}
}
}
void find_scc(int n)
{
dfs_clock = scc_cnt = 0;
memset(sccno,0,sizeof(sccno));
memset(pre,0,sizeof(pre));
memset(out,0,sizeof(out));
memset(in,0,sizeof(in));
for(int i=1;i<=n;i++){
if(!pre[i])dfs(i);
}
if(scc_cnt==1){
cout << "1\n0\n";
return;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(M[i][j]&&sccno[i]!=sccno[j]){
out[sccno[i]]++;
in[sccno[j]]++;
}
}
}
int ans1 = 0,ans2 = 0;
for(int i=1;i<=scc_cnt;i++){
if(in[i]==0)ans1++;
if(out[i]==0)ans2++;
}
cout << ans1 << endl << max(ans1,ans2) << endl;
}
int main()
{
int N = 0;
cin >> N;
int a = 0;
for(int i=1;i<=N;i++){
while(cin >> a&&a){
G[i].push_back(a);
M[i][a] = 1;
}
}
find_scc(N);
return 0;
}