2020/10/11
kuangbin网络流4题
///链接:https://vjudge.net/problem/UVA-10480 /*题意: n个结点,m路径,现在让s和t号结点不要连接,破坏一条点的花费为k。 题解:最小割。但是注意此题有个区别,他是破坏点,所以我们应该拆点,然后在跑。 */ #include"stdio.h" #include"string.h" #include"stack" #include"map" #include"math.h" #include"iostream" #include"string" #include"vector" #include"queue" #include"algorithm" using namespace std; #define OK printf("\n"); #define Debug printf("this_ok\n"); #define INF 1e18 typedef long long ll; #define scanll(a,b) scanf("%lld%lld",&a,&b); #define scanl(a) scanf("%lld",&a); #define printl(a,b) if(b == 0) printf("%lld ",a); else printf("%lld\n",a); #define print_int(a,b) if(b == 0) printf("%d ",a); else printf("%d\n",a); typedef pair<int,int> PII; inline int read(){ int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar(); } return s * w; } const ll mod = 998244353; const int N = 200000,M = 800000; const double pi = acos(-1); const int inf = 1 << 29; const int dirx[4] = {-1,0,1,0}; const int diry[4] = {0,1,0,-1}; int n,m,t,s,tot; ll maxflow,sum; int head[N],ver[M],Next[M],edge[M],d[N]; int w[N]; queue<int> q; map<string,int> Q; void add(int x,int y,int z){ ver[++ tot] = y; Next[tot] = head[x]; edge[tot] = z; head[x] = tot; ver[++ tot] = x; edge[tot] = 0; Next[tot] = head[y]; head[y] = tot; } void add1(int x,int y,int z){ ver[++ tot] = y; Next[tot] = head[x]; edge[tot] = z; head[x] = tot; ver[++ tot] = x; edge[tot] = 0; Next[tot] = head[y]; head[y] = tot; } bool bfs(){ memset(d,0,sizeof(d)); while(q.size())q.pop(); q.push(s); d[s] = 1; while(q.size()){ int x = q.front(); q.pop(); for(int i = head[x]; i; i = Next[i]) if(edge[i] && !d[ver[i]]){ q.push(ver[i]); d[ver[i]] = d[x] + 1; if(ver[i] == t) return 1; } } return 0; } int dinic(int x,ll flow){ if(x == t) return flow; ll rest = flow,k; for(int i = head[x]; i && rest; i = Next[i]){ if(edge[i] && d[ver[i]] == d[x] + 1){ k = dinic(ver[i],min(rest,(ll)edge[i])); if(!k) d[ver[i]] = 0; edge[i] -= k; edge[i ^ 1] += k; rest -= k; } } return flow - rest; } int main(){ while(~scanf("%d",&n)){ m = read(); for(int i = 0; i <= n + n + 2; i ++) head[i] = 0; tot = 1;s = read(); t = read();t += n; for(int i = 1; i <= n; i ++){ int x = read(); add1(i,i + n,x); } for(int i = 1; i <= m; i ++){ int x = read(),y = read(); add(x + n,y,inf); add(y + n,x,inf); } ll flow = 0; maxflow = 0; while(bfs()) while(flow = dinic(s,inf)) maxflow += flow; printf("%lld\n",maxflow); } } /* 3 1 2 3 2 2 6 */ ///链接:https://vjudge.net/problem/UVA-10480 /*题意: n个结点,m路径,现在让1和2号结点不要连接,破坏一条路径的花费为k。 题解:最小割,输出路径的话,x和y一开始有路径,x只和1号点连接,y只和2号点连接,那么就是割边。 鉴于此,本题我们用邻接矩阵存储更为方便; */ #include"stdio.h" #include"string.h" #include"stack" #include"map" #include"math.h" #include"iostream" #include"vector" #include"queue" #include"algorithm" using namespace std; #define OK printf("\n"); #define Debug printf("this_ok\n"); #define INF 0x3f3f3f3f typedef long long ll; #define scanll(a,b) scanf("%lld%lld",&a,&b); #define scanl(a) scanf("%lld",&a); #define printl(a,b) if(b == 0) printf("%lld ",a); else printf("%lld\n",a); #define print_int(a,b) if(b == 0) printf("%d ",a); else printf("%d\n",a); typedef pair<int,int> PII; inline int read(){ int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar(); } return s * w; } const ll mod = 998244353; const int N = 500010,M = 500010; const int inf = 1 << 29; const int maxn=1e3+10; int n,m; int mp[maxn][maxn];//初始流量 int flow[maxn][maxn];//最大流 int path[maxn];//记录路径 int a[maxn];//i点在最大流中的流量 int start,End; int x[maxn],y[maxn]; int maxflow() { queue<int> q; memset(flow,0,sizeof flow); int max_flow=0; while(true){ memset(a,0,sizeof a);//清空标记 a[start]=INF;//源点权值无限大 while(!q.empty()) q.pop(); q.push(start); while(!q.empty()){ int temp=q.front(); q.pop(); for(int i=1;i<=n;i++){ //未走过且流量小于他的权值 if(!a[i]&&flow[temp][i]<mp[temp][i]) { path[i]=temp;//记录前一个点 a[i]=min(a[temp],mp[temp][i]-flow[temp][i]); q.push(i); } } } if(a[End]==0) break; //更新剩余网络 for(int i=End;i!=start;i=path[i]){ flow[path[i]][i]+=a[End];//反向边加 flow[i][path[i]]-=a[End];//正向边减 } max_flow+=a[End]; } return max_flow; } int main() { while(~scanf("%d%d",&n,&m)){ if(n == 0) break; memset(mp,0,sizeof mp); for(int i=1;i<=m;i++) { int u = read(),v = read(),w = read(); mp[u][v]=mp[v][u]=w;///无向路径 x[i]=u; y[i]=v; } start=1;End=2; int flow=maxflow(); for(int i=1;i<=m;i++){//枚举每一条边的两个点 //有一点的权值为0,说明当前点被删除 if((!a[x[i]]&&a[y[i]])||(a[x[i]]&&!a[y[i]])) printf("%d %d\n",x[i],y[i]); } printf("\n"); } return 0; } ///链接:http://acm.hdu.edu.cn/showproblem.php?pid=4280 /*题意: 在最西边的点走到最东边的点最大容量。 题解:ISAP模板题,Dinic过不了。 */ #include"stdio.h" #include"string.h" #include"stack" #include"map" #include"math.h" #include"iostream" #include"string" #include"vector" #include"queue" #include"algorithm" using namespace std; #define OK printf("\n"); #define Debug printf("this_ok\n"); #define INF 1e18 typedef long long ll; #define scanll(a,b) scanf("%lld%lld",&a,&b); #define scanl(a) scanf("%lld",&a); #define printl(a,b) if(b == 0) printf("%lld ",a); else printf("%lld\n",a); #define print_int(a,b) if(b == 0) printf("%d ",a); else printf("%d\n",a); typedef pair<int,int> PII; inline int read(){ int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar(); } return s * w; } const ll mod = 998244353; const int N = 200000,M = 800000; const double pi = acos(-1); const int inf = 1 << 29; const int dirx[4] = {-1,0,1,0}; const int diry[4] = {0,1,0,-1}; int n,m,t,s,tot; ll maxflow,sum; int head[N],ver[M],Next[M],edge[M],d[N]; int w[N]; queue<int> q; map<string,int> Q; void add(int x,int y,int z){ ver[++ tot] = y; Next[tot] = head[x]; edge[tot] = z; head[x] = tot; ver[++ tot] = x; edge[tot] = z; Next[tot] = head[y]; head[y] = tot; } bool bfs(){ memset(d,0,sizeof(d)); while(q.size())q.pop(); q.push(s); d[s] = 1; while(q.size()){ int x = q.front(); q.pop(); for(int i = head[x]; i; i = Next[i]) if(edge[i] && !d[ver[i]]){ q.push(ver[i]); d[ver[i]] = d[x] + 1; if(ver[i] == t) return 1; } } return 0; } int dinic(int x,ll flow){ if(x == t) return flow; ll rest = flow,k; for(int i = head[x]; i && rest; i = Next[i]){ if(edge[i] && d[ver[i]] == d[x] + 1){ k = dinic(ver[i],min(rest,(ll)edge[i])); if(!k) d[ver[i]] = 0; edge[i] -= k; edge[i ^ 1] += k; rest -= k; } } return flow - rest; } int main(){ int T = read(); while(T --){ n = read(); m = read(); tot = 1;s = 1; t = 1; int tmax = -inf, tmin = inf; s = t= 1; for(int i = 1; i <= n; i ++) { int x = read(),y = read(); if(x >= tmax) tmax = x, t = i; if(x <= tmin) tmin = x, s = i; head[i] = 0; } for(int i = 1; i <= m; i ++){ int x = read(),y = read(),w = read(); add(x,y,w); } ll flow = 0; maxflow = 0; while(bfs()) while(flow = dinic(s,inf)) maxflow += flow; printf("%lld\n",maxflow); } } /* 3 1 2 3 2 2 6 #include <cstdio> #include <algorithm> #include <iostream> #include <cstring> #include <string> #include <cmath> #include <queue> #include <vector> #include <map> #include <set> using namespace std; #define INF 0x3f3f3f3f #define N 100010 typedef long long LL; struct Edge { int u, v; LL cap; Edge () {} Edge (int u, int v, LL cap) : u(u), v(v), cap(cap) {} }edge[N*4]; vector<int> G[N]; int dis[N], cur[N], num[N], pre[N], tot, S, T; void Add(int u, int v, int cap) { edge[tot] = Edge(u, v, cap); G[u].push_back(tot++); edge[tot] = Edge(v, u, cap); G[v].push_back(tot++); } int BFS() { // 逆向BFS memset(dis, -1, sizeof(dis)); queue<int> que; que.push(T); dis[T] = 0; while(!que.empty()) { int u = que.front(); que.pop(); for(int i = 0; i < G[u].size(); i++) { Edge &e = edge[G[u][i]]; if(dis[e.v] == -1) { dis[e.v] = dis[u] + 1; que.push(e.v); } } } return ~dis[S]; } int DFS() { // 通过pre数组回溯更新流量 int u = T; int flow = INF; while(u != S) { Edge &e = edge[pre[u]]; if(e.cap < flow) flow = e.cap; u = e.u; } u = T; while(u != S) { Edge& e1 = edge[pre[u]]; Edge& e2 = edge[pre[u]^1]; e1.cap -= flow; e2.cap += flow; u = e1.u; } return flow; } int ISAP(int n) { if(!BFS()) return 0; // 从汇点到源点逆向BFS初始化dis数组 memset(num, 0, sizeof(num)); memset(cur, 0, sizeof(cur)); // 当前弧优化 for(int i = 1; i <= n; i++) if(~dis[i]) num[dis[i]]++; // 当前距离为dis[i]的结点数 int ans = 0, u = S; while(dis[S] < n) { if(u == T) ans += DFS(), u = S; // 回溯之前的结点并把更新流量 int flag = 0; for(int i = 0; i < G[u].size(); i++) { Edge &e = edge[G[u][i]]; if(dis[e.v] + 1 == dis[u] && e.cap > 0) { // 可以增广 pre[e.v] = G[u][i]; cur[u] = i; flag = 1; u = e.v; break; } } if(!flag) { // 不能增广,retreat if(--num[dis[u]] == 0) break; // gap优化 int md = n; for(int i = 0; i < G[u].size(); i++) { Edge &e = edge[G[u][i]]; if(e.cap > 0 && dis[e.v] < md) { md = dis[e.v]; cur[u] = i; } } num[dis[u] = md + 1]++; if(u != S) u = edge[pre[u]].u; } } return ans; } int main() { int t; scanf("%d", &t); while(t--) { int n, m; scanf("%d%d", &n, &m); tot = 0; for(int i = 0; i <= n; i++) G[i].clear(); int u, v, c, west = 100000000, east = -10000000; for(int i = 1; i <= n; i++) { scanf("%d%d", &u, &v); if(west > u) west = u, S = i; if(east < u) east = u, T = i; } for(int i = 1; i <= m; i++) { scanf("%d%d%d", &u, &v, &c); Add(u, v, c); // 无向图的话反向边的cap也是c } printf("%d\n", ISAP(n)); } return 0; } */ ///链接:https://vjudge.net/problem/POJ-1087 /*题意: 有m个设备需要插座(给出了每个设备需要的插座型号),但是现在只有n个插座(任意两个插座类型不相同,因为题目说每种类型插座就一个),且给你k个转换器(转换器(u,v)可以使得插座v转接到u上),问你最多有几个设备没有插座可用? 题解:建图: 源点s(编号0)到每个设备i有边 (s,i,1). 每个插座到j到汇点t(编号n+m+1)有边 (j,t,1) 如果设备i对应的插座是j,那么有边(i,j,1) 如果转换器对应(u,v)(说明可以把插座v转接到u上),那么有边(u,v,INF) (因为转换器无限个,且它可以把原本需要u插座的电器连到v插座上) */ #include"stdio.h" #include"string.h" #include"stack" #include"map" #include"math.h" #include"iostream" #include"string" #include"vector" #include"queue" #include"algorithm" using namespace std; #define OK printf("\n"); #define Debug printf("this_ok\n"); #define INF 1e18 typedef long long ll; #define scanll(a,b) scanf("%lld%lld",&a,&b); #define scanl(a) scanf("%lld",&a); #define printl(a,b) if(b == 0) printf("%lld ",a); else printf("%lld\n",a); #define print_int(a,b) if(b == 0) printf("%d ",a); else printf("%d\n",a); typedef pair<int,int> PII; inline int read(){ int s = 0, w = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') w = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar(); } return s * w; } const ll mod = 998244353; const int N = 50010,M = 300010; const double pi = acos(-1); const int inf = 1 << 29; const int dirx[4] = {-1,0,1,0}; const int diry[4] = {0,1,0,-1}; int n,m,t,s,tot; ll maxflow,sum; int head[N],ver[M],Next[M],edge[M],d[N]; int w[N]; queue<int> q; map<string,int> Q; void add(int x,int y,int z){ ver[++ tot] = y; Next[tot] = head[x]; edge[tot] = z; head[x] = tot; ver[++ tot] = x; edge[tot] = 0; Next[tot] = head[y]; head[y] = tot; } bool bfs(){ memset(d,0,sizeof(d)); while(q.size())q.pop(); q.push(s); d[s] = 1; while(q.size()){ int x = q.front(); q.pop(); for(int i = head[x]; i; i = Next[i]) if(edge[i] && !d[ver[i]]){ q.push(ver[i]); d[ver[i]] = d[x] + 1; if(ver[i] == t) return 1; } } return 0; } int dinic(int x,ll flow){ if(x == t) return flow; ll rest = flow,k; for(int i = head[x]; i && rest; i = Next[i]){ if(edge[i] && d[ver[i]] == d[x] + 1){ k = dinic(ver[i],min(rest,(ll)edge[i])); if(!k) d[ver[i]] = 0; edge[i] -= k; edge[i ^ 1] += k; rest -= k; } } return flow - rest; } int main(){ n = read(); tot = 1;int top = 0; s = 0; t = 510; int p1[120] = {0},p2[120] = {0}; for(int i = 1; i <= n; i ++){ char str; cin >> str; p1[str - 'A' + 1] ++; } for(int i = 1; i < 120; i ++){ if(!p1[i]) continue; add(i,t,p1[i]); } m = read(); for(int i = 1; i <= m; i ++){ string name; char str; cin >> name >> str; p2[str - 'A' + 1] ++; } for(int i = 1; i < 120; i ++){ if(!p2[i]) continue; add(s,i + 120,p2[i]); // add(i + 120,i,inf); } int q = read(); for(int i = 1; i <= q; i ++){ char s1,s2; cin >> s1 >> s2; add(s1 - 'A' + 1 + 120,s2 - 'A' + 1 + 120,inf); } for(int i = 1; i < 120; i ++) add(i + 120,i,inf); ll flow = 0; while(bfs()) while(flow = dinic(s,inf)) maxflow += flow; printf("%lld\n",m - maxflow); } /* 3 1 2 3 2 2 6 */