网络流之二者取一式问题
网络流之二者取一式问题:
大致是说有n个人,有两个阵营,把人分成相应的阵营会有对应的价值,可能还会有联立的价值(就是规定如果u号选了A,v号选了B就有额外的价值什么的);
这种问题往往是转换为最小割问题,然后跑出最大流,如果题目要求是求最大的价值那就把全部边的答案相加在减去最大流
1 ) https://www.luogu.org/problem/P1361
题目描述
小M在MC里开辟了两块巨大的耕地A和B(你可以认为容量是无穷),现在,小P有n中作物的种子,每种作物的种子有1个(就是可以种一棵作物)(用1...n编号)。
现在,第i种作物种植在A中种植可以获得ai的收益,在B中种植可以获得bi的收益,而且,现在还有这么一种神奇的现象,就是某些作物共同种在一块耕地中可以获得额外的收益,小M找到了规则中共有m种作物组合,第i个组合中的作物共同种在A中可以获得c1i的额外收益,共同总在B中可以获得c2i的额外收益。
小M很快的算出了种植的最大收益,但是他想要考考你,你能回答他这个问题么?
输入格式
第一行包括一个整数n
第二行包括n个整数,表示ai第三行包括n个整数,表示bi第四行包括一个整数m接下来m行,
对于接下来的第i行:第一个整数ki,表示第i个作物组合中共有ki种作物,
接下来两个整数c1i,c2i,接下来ki个整数,表示该组合中的作物编号。
输出格式
只有一行,包括一个整数,表示最大收益
题解:(1)https://www.cnblogs.com/butterflydew/p/9240393.html (2)https://blog.csdn.net/yzyyylx/article/details/79349244
#include<bits/stdc++.h> using namespace std; const int maxn = 1e6 + 10; const int inf = 0x3f3f3f3f; struct Edge { int from,to,cap,flow; Edge(){} Edge(int from,int to,int cap,int flow):from(from),to(to),cap(cap),flow(flow){} }; struct Dinic { int m,s,t; //结点数,边数(包括反向弧),源点与汇点编号 vector<Edge> edges; //边表 edges[e]和edges[e^1]互为反向弧 vector<int> G[maxn]; //邻接表,G[i][j]表示结点i的第j条边在e数组中的序号 bool vis[maxn]; //BFS使用,标记一个节点是否被遍历过 int d[maxn]; //d[i]表从起点s到i点的距离(层次) int cur[maxn]; //cur[i]表当前正访问i节点的第cur[i]条弧 void init(int n,int s,int t) { this->s=s,this->t=t; 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]=true; 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]=true; d[e.to] = d[x]+1; Q.push(e.to); } } } return vis[t]; } //a表示从s到x目前为止所有弧的最小残量 //flow表示从x到t的最小残量 int DFS(int x,int a) { if(x==t || a==0)return a; int flow=0,f;//flow用来记录从x到t的最小残量 for(int& i=cur[x]; i<G[x].size(); i++)///注意这里的&符号,这样i增加的同时也能改变cur[u]的值,达到记录当前弧的目的 { 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; } } if(!flow) d[x] = -1;///炸点优化 return flow; } int Maxflow() { int flow=0; while(BFS()) { memset(cur,0,sizeof(cur)); flow += DFS(s,inf); } return flow; } }DC; int sum,w; int main(void ) { int n,m;scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&w); sum+=w; DC.AddEdge(0,i,w); } for(int i=1 ; i<=n ; i++){ scanf("%d",&w); sum+=w; DC.AddEdge(i,n+1,w); } scanf("%d",&m); for(int i=1 ; i<=m ; i++){ int k,c1,c2; scanf("%d%d%d",&k,&c1,&c2); DC.AddEdge(0,i+n+1,c1);sum+=c1; DC.AddEdge(i+n+m+1,n+1,c2);sum+=c2; for(int j=1 ; j<=k ; j++){ int v; scanf("%d",&v); DC.AddEdge(i+n+1,v,inf); DC.AddEdge(v,i+n+m+1,inf); } } DC.s=0;DC.t=n+1; printf("%d",sum-DC.Maxflow()); }
2)http://acm.hdu.edu.cn/showproblem.php?pid=6598
题意是说一个军队有n人,你可以给他们每个人安排战士或者法师的职业,有m对人有组合技,组合技的信息是A,B,C,代表如果这两个人是两个战士,则组合技威力为A,一个战士一个法师,威力为B,其中B=A/4+C/3,两个法师,威力为C,求最大的威力。
题解:(1)https://www.cnblogs.com/isakovsky/p/11248963.html
(2)https://www.cnblogs.com/sainsist/p/11328601.html
#include <bits/stdc++.h> using namespace std; #define rep(i,a,n) for (int i=a;i<n;i++) #define per(i,a,n) for (int i=n-1;i>=a;i--) #define pb push_back #define mp make_pair #define all(x) (x).begin(),(x).end() #define fi first #define se second #define SZ(x) ((int)(x).size()) typedef vector<int> VI; typedef long long ll; typedef pair<int,int> PII; mt19937 mrand(random_device{}()); const ll mod=1000000007; int rnd(int x) { return mrand() % x;} ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;} ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;} const int maxn = 1e3+100; const int maxm = 1e5+100; const int inf = 0x7f7f7f7f; typedef struct Dinic { typedef struct Edge { int u,v,w,nxt; } Edge; int head[maxn],hcnt; int dep[maxn]; int cur[maxn]; Edge e[maxm]; int S,T,N; void init() { memset(head,-1,sizeof head); hcnt = 0; S = T = N = 0; } void adde(int u,int v,int w) { e[hcnt].u = u,e[hcnt].v = v,e[hcnt].w = w; e[hcnt].nxt = head[u];head[u] = hcnt++; e[hcnt].u = v,e[hcnt].v = u,e[hcnt].w = 0; e[hcnt].nxt = head[v];head[v] = hcnt++; } int bfs() { rep(i,0,N) { dep[i] = inf; } queue<int> q; q.push(S); dep[S] = 0; while(!q.empty()) { int u = q.front();q.pop(); for(int i = head[u];~i;i = e[i].nxt) { int v = e[i].v,w = e[i].w; if(w > 0 && dep[u] + 1 < dep[v]) { dep[v] = dep[u] + 1; if(v == T) { return 1; } q.emplace(v); } } } return dep[T] != inf; } int dfs(int s,int mw) { if(s == T) return mw; for(int i = cur[s];~i;i=e[i].nxt) { cur[s] = i; int v = e[i].v,w=e[i].w; if(w <= 0 || dep[v] != dep[s] + 1) { continue; } int cw = dfs(v,min(w,mw)); if(cw <= 0) continue; e[i].w -= cw; e[i^1].w += cw; return cw; } return 0; } ll dinic() { ll res = 0; while(bfs()) { rep(i,0,N) { cur[i] = head[i]; } while(int d = dfs(S,inf)) { res += 1ll * d; } } return res; } } Dinic; int n,m,s,t; ll w[maxn],ww[maxn]; int main(int argc, char const *argv[]) { while(scanf("%d%d",&n,&m)!=EOF) { memset(w,0,sizeof w);memset(ww,0,sizeof ww); Dinic din;din.init(); s = 0,t = n+1; din.S = 0,din.T = n+1,din.N = n+10; int u,v; ll a,b,c; ll ans = 0; rep(i,0,m) { scanf("%d%d%lld%lld%lld",&u,&v,&a,&b,&c); // cout << "---------" << endl; w[u] += b+c;w[v]+=b+c; ww[u] += a+b;ww[v]+=a+b; din.adde(u,v,a+c-2*b); din.adde(v,u,a+c-2*b); ans += a + b + c; } // cout << ans << endl; rep(i,1,n+1) { din.adde(s,i,w[i]); din.adde(i,t,ww[i]); } printf("%lld\n",ans-din.dinic()/2 ); } return 0; }
题目做的多了就有了呀