点这里 |
---|
题意: 给定一个无向图,n个点和m条边,求最西边的点到最东边的点的最大流。
题解: 无向图区别与有向图,只要正反都加上权值相同的边即可。点比较多的图适合用ISAP算法(因为我还没学,就用了Dinic)。代价就是超时过不去,看到网上说把queue改成数组能省一些时间,但我不太会。而是把存边的结构体改成了数组,确实要比结构体快一些。(相同的代码,提交了三次,还是TLE了一次。可能还是侥幸AC了吧)
过程中犯的错:
- TLE: 因为还没学ISAP就先用Dinic了,数组要比结构体更快!
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 10;
int T, n, m, cnt, k;
int s, t, smin, tmax;
int cur[N];
int head[N]; //链式前向星存图
int depth[N];
int W[2 * N]; //第i条边的(残留)容量
int V[2 * N]; //第i条边的终点
int Next[2 * N];//第i条边的的起点的下一条边的编号
void init(){
cnt = 0, s = t = 1;
smin = inf, tmax = -inf;
for(int i = 1; i <= n; i++) head[i] = -1;
for(int i = 0; i <= 2 * n; i++) Next[i] = -1;
}
void add(int u, int v, int w){ V[cnt] = v, W[cnt] = w, Next[cnt] = head[u], head[u] = cnt++;}
void addedge(int u, int v, int w){ add(u, v, w); add(v, u, w);} //无向图,正反加相同权值的边
bool bfs(){ //如果是有向图则改成 add(u, v, w); add(v, u, 0);
for(int i = 1; i <= n; i++) depth[i] = 0;
queue<int> Q;
Q.push(s); depth[s] = 1;
while(!Q.empty()){
int u = Q.front(); Q.pop();
for(int i = head[u]; ~i; i = Next[i]){
if(depth[V[i]] == 0 && W[i] > 0){ //某点没有去过,并且还有残留容量
depth[V[i]] = depth[u] + 1;
Q.push(V[i]);
}
}
}
if(depth[t] > 0) return 1; //至少能流到终点
return 0;
}
int dfs(int u, int flow){
if(u == t) return flow;
for(int& i = cur[u]; ~i; i = Next[i]){ //&符号,修改i的同时也修改了cur[u]
if(depth[V[i]] == depth[u] + 1 && W[i] != 0){
int minflow = dfs(V[i], min(flow, W[i]));
if(minflow > 0){
W[i] -= minflow;
W[i ^ 1] += minflow; //更新残留容量
return minflow;
}
}
}
return 0;
}
int Dinic(){
int ans = 0;
while(bfs()){
for(int i = 1; i <= n; i++) cur[i] = head[i]; //每次都要复原
while(int d = dfs(s, inf)) ans += d;
}
return ans;
}
int main(){
scanf("%d", &T);
while(T--){
scanf("%d%d", &n, &m);
init();
for(int i = 1; i <= n; i++){
int x, y;
scanf("%d%d", &x, &y);
if(x >= tmax) tmax = x, t = i;
if(x <= smin) smin = x, s = i;
}
while(m--){
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
addedge(u, v, w);
}
printf("%d\n", Dinic());
}
return 0;
}