Codeforces 最大流 费用流
这套题目做完后,一定要反复的看!
代码经常出现的几个问题:
本机测试超时:
1.init函数忘记写。
2.addedge函数写成add函数。
3.边连错了。
代码TLE:
1.前向星边数组开小.
2.用了memset,慎用。
1. CodeForces 498C Array and Operations
我发现cf上的网络流的建图思路都非常好,准备着重练习一下。
此题枚举每一个质因子,对每个质因子建图,确实是很好的思路。
#include <bits/stdc++.h>
using namespace std; #define next Next
const int inf = 0x3f3f3f3f;
const int maxn=;
int level[maxn];
int iter[maxn];
int head[maxn],tot;
struct edge{
int to,cap,Next;
} e[]; ///此处应为边的两倍,加一条容量为0的反向边
void init(int n){ for(int i = ; i <= n; i++) head[i] = -;
tot=;
}
void add(int from,int to,int cap){
e[tot].Next=head[from];
e[tot].to=to;
e[tot].cap=cap;
head[from]=tot;
tot++;
}
void addedge(int from,int to,int cap){
add(from,to,cap);
add(to,from,);
}
void bfs(int s){
memset(level,-,sizeof(level));
queue<int> q;
level[s]=;
q.push(s);
while(!q.empty()){
int v=q.front(); q.pop();
for(int i=head[v];~i;i=e[i].Next){
edge &ed=e[i];
if(ed.cap>&&level[ed.to]<){
level[ed.to]=level[v]+;
q.push(ed.to);
}
}
}
}
int dfs(int v,int t,int f){
if(v==t) return f;
for(int &i=iter[v];~i;i=e[i].Next){
edge &ed=e[i];
if(ed.cap>&&level[v]<level[ed.to]){
int d=dfs(ed.to,t,min(f,ed.cap));
if(d>){
ed.cap-=d;
e[i^].cap+=d;
return d;
}
}
}
return ;
}
int max_flow(int s,int t){
int flow=;
while(){
bfs(s);
if(level[t]<) return flow;
memcpy(iter,head,sizeof(iter));
int f;
while((f=dfs(s,t,inf))>){
flow+=f;
}
}
} //线性素数筛
const int N = 1e5 + ;
int prime[N], num_prime = ;
int isNotPrime[N];
void is_prime(int N)
{
isNotPrime[] = isNotPrime[] = ;
for(int i=;i<N;i++)
{
if(!isNotPrime[i])
{
prime[num_prime++] = i;
}
for(int j=;j<num_prime&&i*prime[j]<N;j++)
{
isNotPrime[i*prime[j]] = ;
if(!(i%prime[j]))
{
break;
}
}
}
return;
} int a[];
vector<int> p[];
vector<int> cnt[];
int l[][];
vector<int> big; ///大素数
int n, m;
int solve(int ans)
{
init(n + );
int s = , t = n + ;
for(int j = ; j <= n; j++)
{
int pos = lower_bound(p[j].begin(), p[j].end(), ans) - p[j].begin();
if(pos < p[j].size() && p[j][pos] == ans)
{
// printf("%d %d %d\n", j, p[j][pos], cnt[j][pos]);
if(j & ) addedge(s, j, cnt[j][pos]);
else addedge(j, t, cnt[j][pos]);
}
}
for(int j = ; j <= m; j++)
{
int tmp = ;
for(int k = ; k < ; k++)
{
// printf("%d\n", l[j][k]);
int pos = lower_bound(p[l[j][k]].begin(), p[l[j][k]].end(), ans) - p[l[j][k]].begin();
// printf("%d %d %d\n", j, pos, cnt[l[j][k]][pos]);
if(pos < p[l[j][k]].size() && p[l[j][k]][pos] == ans)
{
tmp = min(tmp, cnt[l[j][k]][pos]);
}
else tmp = ;
}
// printf("%d %d %d\n", l[j][0], l[j][1], tmp);
addedge(l[j][], l[j][], tmp);
}
// return 0;
return max_flow(s, t);
}
int main()
{
is_prime(1e5 + );
scanf("%d %d", &n, &m);
for(int i = ; i <= n; i++) scanf("%d", &a[i]);
for(int i = ; i <= n; i++)
{
for(int j = ; j <= 1e5; j++)
{
if(j > a[i]) break;
if(a[i] % j == ) p[i].push_back(j);
int tmp = ;
while(a[i] % j == )
{
tmp++;
a[i] /= j;
}
if(tmp) cnt[i].push_back(tmp);
}
if(a[i] > ) p[i].push_back(a[i]), cnt[i].push_back();
}
for(int i = ; i <= m; i++)
{
int go1, go2; scanf("%d %d", &go1, &go2);
if(go1 % == ) swap(go1, go2);
l[i][] = go1, l[i][] = go2;
}
int ans = ;
for(int i = ; i < num_prime; i++)
{
ans += solve(prime[i]); }
for(int i = ; i <= n; i++)
{
int t = p[i].size();
if(t == ) continue;
if(p[i][t - ] >= 1e5) big.push_back(p[i][t - ]);
}
sort(big.begin(), big.end());
big.erase(unique(big.begin(), big.end()), big.end());
for(int i = ; i < (int)big.size(); i++)
{
ans += solve(big[i]);
}
printf("%d\n", ans);
return ;
}
/*
3 2
6 12 8
1 2
2 3
*/
Code
2. CodeForces 884FAnti-Palindromize
好图。
题目保证反回文串一定有解,就保证下面这样建图的正确性。
在26个字母和源$S$之间连<字母出现的次数,费用0>的边。 建$\frac{n}{2}$个新的结点,表示$i$到$n-i+1$的映射关系。从这$\frac{n}{2}$个节点向汇$T$连<容量为2,费用为0>的边。
然后对于每一个字母$c$,分三种情况连边:
如果字母$c$在结点$i$所对应的$i$到$n-i+1$中出现过两次,那么在$c$和$i$之间连一条<容量为1,费用为$max(b[i], b[n - i + 1]$>的边。(当然最后留下最大的一个)
如果出现过一次,那么在$c$和$i$之间连一条<容量为1,费用为那一次的$b$>的边。
如果没有出现过,在$c$和$i$之间连一条<容量为1,费用为0>的边。
#include <bits/stdc++.h>
using namespace std;
const int maxn = ;
const int INF = 1e9;
int dist[maxn];
int pv[maxn],pe[maxn];
struct edge
{
int to, cap, rev;
int cost;
edge(int a, int b, int c, int d)
{
to = a, cap = b, cost = c, rev = d;
}
};
vector<edge> g[maxn];
void addedge(int from,int to,int cap,int cost)
{
g[from].push_back(edge(to,cap,cost,g[to].size()));
g[to].push_back(edge(from,,-cost,g[from].size()-));
}
int vis[maxn];
void SPFA(int s, int t)
{
for(int i = ; i < maxn; i++) dist[i] = INF;
memset(vis, , sizeof(vis));
dist[s] = , vis[s] = ;
queue<int> q;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = ;
for(int i = ; i < g[u].size(); i++)
{
edge &e = g[u][i];
if(e.cap > && (dist[e.to] - (dist[u] + e.cost)) > )
{
pv[e.to] = u, pe[e.to] = i;
dist[e.to] = dist[u] + e.cost;
if(!vis[e.to])
{
vis[e.to] = ;
q.push(e.to);
}
}
}
}
}
int min_cost_flow(int s,int t,int f,int& max_flow)
{
int ret = 0.0;
while(f>)
{
SPFA(s, t);
if(dist[t] == INF) return ret;///同一目的地,每次增广路都是最小费用
///当所有边的流量都流净后,即没有残余网络,返回。
int d = f;
for(int v=t;v!=s;v=pv[v])
{
d = min(d,g[pv[v]][pe[v]].cap);
}
f -= d;
max_flow += d;
ret += (int)d*dist[t]; ///走一单位就消耗dist[t]
for(int v=t;v!=s;v=pv[v])
{
edge &e = g[pv[v]][pe[v]];
e.cap -= d;
g[v][e.rev].cap += d;
}
}
return ret;
} char s[];
int b[];
int cnt[];
int main()
{ int n; scanf("%d", &n);
scanf("%s", s + );
for(int i = ; i <= n; i++) scanf("%d", &b[i]);
for(int i = ; i <= n; i++)
{
cnt[s[i] - 'a' + ]++;
}
int S = + n / + , T = S + ;
for(int i = ; i <= ; i++)
{
addedge(S, i, cnt[i], );
// printf("%c %d\n", i + 'a' - 1, cnt[i]);
if(cnt[i])
{
for(int j = ; j <= n / ; j++)
{
char c = i + 'a' - ;
int flag = ;
if(s[j] == c) flag++;
if(s[n - j + ] == c) flag++;
if(!flag) addedge(i, + j, , );
else if(flag == )
{
if(s[j] == c) addedge(i, + j, , -b[j]);
else addedge(i, + j, , -b[n - j + ]);
}
else
{
addedge(i, + j, , min(-b[j], -b[n - j + ]));
}
}
}
}
for(int j = ; j <= n / ; j++)
{
addedge(j + , T, , );
}
int maxflow = ;
int ans = min_cost_flow(S, T, INF, maxflow);
printf("%d\n", -ans);
return ;
}
Code
3. CodeForces 164C Machine Programming
算了好几次head大小愣是算不对,这题前面出过好几次了。 焦作F
这个输出路径还是用前向星好,每条边都有索引,方便判断$e.cap$是否用完。
#include <bits/stdc++.h>
using namespace std;
const int maxn = ;
const int INF = 1e9;
int dist[maxn];
int pv[maxn],pe[maxn];
struct edge
{
int to, cap, pre;
int cost;
}e[];
int tot = , head[maxn];
void init(int n)
{
tot = ;
fill(head, head + n + , -);
}
void add(int from,int to,int cap,double cost)
{
e[tot].pre = head[from];
e[tot].to = to;
e[tot].cap = cap;
e[tot].cost = cost;
head[from] = tot++;
}
void addedge(int from,int to,int cap,double cost)
{
add(from,to,cap,cost);
add(to,from,,-cost);
}
int vis[maxn];
void SPFA(int s, int t)
{
for(int i = ; i < maxn; i++) dist[i] = INF;
memset(vis, , sizeof(vis));
dist[s] = , vis[s] = ;
queue<int> q;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = ;
for(int i = head[u]; ~i; i = e[i].pre)
{
int to = e[i].to, cap = e[i].cap;
if(cap > && (dist[to] - (dist[u] + e[i].cost)) > )
{
pv[to] = u, pe[to] = i;
dist[to] = dist[u] + e[i].cost;
if(!vis[to])
{
vis[to] = ;
q.push(to);
}
}
}
}
}
int min_cost_flow(int s,int t,int f,int& max_flow)
{
int ret = 0.0;
while(f>)
{
SPFA(s, t);
if(dist[t] == INF) return ret;///同一目的地,每次增广路都是最小费用
///当所有边的流量都流净后,即没有残余网络,返回。
int d = f;
for(int v=t;v!=s;v=pv[v])
{
d = min(d, e[pe[v]].cap);
}
f -= d;
max_flow += d;
ret += (int)d*dist[t]; ///走一单位就消耗dist[t]
for(int v=t;v!=s;v=pv[v])
{
e[pe[v]].cap -= d;
e[pe[v]^].cap += d;
}
}
return ret;
}
int s[], t[], c[], x[];
int main()
{
init();
int n, k; scanf("%d %d", &n, &k);
int cnt = ;
for(int i = ; i <= n; i++)
{
scanf("%d %d %d", &s[i], &t[i], &c[i]);
t[i] = s[i] + t[i];
x[cnt++] = s[i], x[cnt++] = t[i];
}
sort(x, x + cnt);
cnt = unique(x,x + cnt) - x;
for(int i = ; i <= n; i++)
{
s[i] = lower_bound(x, x + cnt, s[i]) - x;
t[i] = lower_bound(x, x + cnt, t[i]) - x;
}
int S = cnt + , T = S + ;
for(int i = ; i <= n; i++)
{
addedge(s[i], t[i], , -c[i]);
}
addedge(S, , k, );
for(int i = ; i < cnt; i++)
{
addedge(i, i + , k, );
}
addedge(cnt, T, k, );
int maxflow = ;
min_cost_flow(S, T, INF, maxflow);
for(int i = ; i < * n; i += )
{
if(e[i].cap == ) printf("1 ");
else printf("0 ");
}
return ;
}
Code
4. Gym 100212I Trade
太蠢了,这种构图竟然都想不到……
每个点度数$≥2$,那我只要在源点$S$和$u$之间连一条$deg[u]-2$的边,汇点那边做同样的处理。
这样如果这条边能去掉,就自然的从$S$到$T$流过来了。
然后看正向边有没有流完,就可以去掉了。
#include <bits/stdc++.h>
using namespace std;
#define next Next
const int inf = 0x3f3f3f3f;
const int maxn=;
int level[maxn];
int iter[maxn];
int head[maxn],tot;
struct edge{
int to,cap,Next;
} e[]; ///此处应为边的两倍,加一条容量为0的反向边
void init(int n){
fill(head, head + n + , -);
tot=;
}
void add(int from,int to,int cap){
e[tot].Next=head[from];
e[tot].to=to;
e[tot].cap=cap;
head[from]=tot;
tot++;
}
void addedge(int from,int to,int cap){
add(from,to,cap);
add(to,from,);
}
void bfs(int s){
memset(level,-,sizeof(level));
queue<int> q;
level[s]=;
q.push(s);
while(!q.empty()){
int v=q.front(); q.pop();
for(int i=head[v];~i;i=e[i].Next){
edge &ed=e[i];
if(ed.cap>&&level[ed.to]<){
level[ed.to]=level[v]+;
q.push(ed.to);
}
}
}
}
int dfs(int v,int t,int f){
if(v==t) return f;
for(int &i=iter[v];~i;i=e[i].Next){
edge &ed=e[i];
if(ed.cap>&&level[v]<level[ed.to]){
int d=dfs(ed.to,t,min(f,ed.cap));
if(d>){
ed.cap-=d;
e[i^].cap+=d;
return d;
}
}
}
return ;
}
int max_flow(int s,int t){
int flow=;
while(){
bfs(s);
if(level[t]<) return flow;
memcpy(iter,head,sizeof(iter));
int f;
while((f=dfs(s,t,inf))>){
flow+=f;
}
}
} int u[], v[];
int deu[], dev[];
int del[];
int main()
{
freopen("trade.in", "r", stdin);
freopen("trade.out", "w", stdout);
init();
int n, m, k; scanf("%d %d %d", &n, &m, &k);
for(int i = ; i <= k; i++)
{
scanf("%d %d", &u[i], &v[i]);
deu[u[i]]++, dev[v[i]]++;
}
for(int i = ; i <= n; i++)
{
if(deu[i] < )
{
printf("-1\n");
return ;
}
}
for(int i = ; i <= m; i++)
{
if(dev[i] < )
{
printf("-1\n");
return ;
}
}
for(int i = ; i <= k; i++)
{
addedge(u[i], v[i] + n, );
}
int S = n + m + , T = n + m + ;
for(int i = ; i <= n; i++)
{
if(deu[i] >= ) addedge(S, i, deu[i] - );
}
for(int i = ; i <= m; i++)
{
if(dev[i] >= ) addedge(i + n, T, dev[i] - );
}
int ans = max_flow(S, T);
printf("%d\n", k - ans);
for(int i = ; i < * k; i += )
{
if(e[i].cap == ) del[i / + ] = ;
}
for(int i = ; i <= k; i++)
{
if(!del[i]) printf("%d ", i);
}
return ;
}
Code
5. CodeForces 730I Olympiad in Programming and Sports
怎么保证每个点只被用一次?
从p和s连向它,再引出一条流量为1的边。
#include <bits/stdc++.h>
using namespace std;
const int maxn = ;
const int INF = 1e9;
int dist[maxn];
int pv[maxn],pe[maxn];
struct edge
{
int to, cap, pre;
int cost;
}e[];
int tot = , head[maxn];
void init(int n)
{
tot = ;
fill(head, head + n + , -);
}
void add(int from,int to,int cap,double cost)
{
e[tot].pre = head[from];
e[tot].to = to;
e[tot].cap = cap;
e[tot].cost = cost;
head[from] = tot++;
}
void addedge(int from,int to,int cap,double cost)
{
add(from,to,cap,cost);
add(to,from,,-cost);
}
int vis[maxn];
void SPFA(int s, int t)
{
for(int i = ; i < maxn; i++) dist[i] = INF;
memset(vis, , sizeof(vis));
dist[s] = , vis[s] = ;
queue<int> q;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = ;
for(int i = head[u]; ~i; i = e[i].pre)
{
int to = e[i].to, cap = e[i].cap;
if(cap > && (dist[to] - (dist[u] + e[i].cost)) > )
{
pv[to] = u, pe[to] = i;
dist[to] = dist[u] + e[i].cost;
if(!vis[to])
{
vis[to] = ;
q.push(to);
}
}
}
}
}
int min_cost_flow(int s,int t,int f,int& max_flow)
{
int ret = ;
while(f>)
{
SPFA(s, t);
if(dist[t] == INF) return ret;///同一目的地,每次增广路都是最小费用
///当所有边的流量都流净后,即没有残余网络,返回。
int d = f;
for(int v=t;v!=s;v=pv[v])
{
d = min(d, e[pe[v]].cap);
}
f -= d;
max_flow += d;
ret += (int)d*dist[t]; ///走一单位就消耗dist[t]
for(int v=t;v!=s;v=pv[v])
{
e[pe[v]].cap -= d;
e[pe[v]^].cap += d;
}
}
return ret;
} int main()
{ int n, p, s; scanf("%d %d %d", &n, &p, &s);
init(n + );
int l = n + , r = n + , S = n + , T = S + ;
for(int i = ; i <= n; i++)
{
int a; scanf("%d", &a);
addedge(l, i, , -a);
}
for(int i = ; i <= n; i++)
{
int b; scanf("%d", &b);
addedge(r, i, , -b);
}
addedge(S, l, p, ), addedge(S, r, s, );
for(int i = ; i <= n; i++)
{
addedge(i, T, , );
}
int maxflow = ;
int ans = min_cost_flow(S, T, INF, maxflow);
printf("%d\n", -ans);
for(int i = ; i < * n; i += )
{
if(e[i].cap == ) printf("%d ", i / + );
}
printf("\n");
for(int i = * n; i < * n; i += )
{
if(e[i].cap == ) printf("%d ", i / - n + );
}
}
Code
6. Gym 101755D Transfer Window
很开心,接连几道题自己可以独立建图,并且成功AC,说明没有白训练,做 -> 受益 -> 做,形成闭环。
这道题图不是很难建,但是要输出网络流的路径,这就要求对最大流模板很熟悉,改模板。
#include <bits/stdc++.h>
using namespace std;
#define next Next
const int inf = 0x3f3f3f3f;
const int maxn=;
int level[maxn];
int iter[maxn];
int head[maxn],tot;
int pv[maxn],pe[maxn];
struct edge{
int to,cap,Next;
} e[]; ///此处应为边的两倍,加一条容量为0的反向边
void init(int n){
fill(head, head + n + , -);
tot=;
}
void add(int from,int to,int cap){
e[tot].Next=head[from];
e[tot].to=to;
e[tot].cap=cap;
head[from]=tot;
tot++;
}
void addedge(int from,int to,int cap){
add(from,to,cap);
add(to,from,);
}
void bfs(int s){
memset(level,-,sizeof(level));
queue<int> q;
level[s]=;
q.push(s);
while(!q.empty()){
int v=q.front(); q.pop();
for(int i=head[v];~i;i=e[i].Next){
edge &ed=e[i];
if(ed.cap>&&level[ed.to]<){
level[ed.to]=level[v]+;
q.push(ed.to);
}
}
}
}
int dfs(int v,int t,int f){
if(v==t) return f;
for(int &i=iter[v];~i;i=e[i].Next){
edge &ed=e[i];
if(ed.cap>&&level[v]<level[ed.to]){
int d=dfs(ed.to,t,min(f,ed.cap));
if(d>){
pv[ed.to] = v, pe[ed.to] = i;
ed.cap-=d;
e[i^].cap+=d;
return d;
}
}
}
return ;
}
vector<int> out[maxn];
int max_flow(int s,int t){
int flow=;
while(){
bfs(s);
if(level[t]<) return flow;
memcpy(iter,head,sizeof(iter));
int f;
while((f=dfs(s,t,inf))>)
{
int last = ;
for(int v = t; v != s; v = pv[v])
{
last = v;
}
for(int v = pv[t]; v != s; v = pv[v])
{
out[last].push_back(v);
}
flow+=f;
}
}
}
char s[maxn][maxn];
struct node
{
int l, r;
}cur[];
int bel[];
int vis[];
int main()
{
int n, k; scanf("%d %d", &n, &k);
init(n + );
int S = n + , T = S + ;
for(int i = ; i <= k; i++)
{
int a; scanf("%d", &a);
addedge(S, a, );
bel[a]++;
}
for(int i = ; i <= k; i++)
{
int a; scanf("%d", &a);
addedge(a, T, );
}
for(int i = ; i <= n; i++)
{
scanf("%s", s[i] + );
for(int j = ; j <= n; j++)
{
if(s[i][j] == '')
{
addedge(i, j, inf);
}
}
}
int ans = max_flow(S, T);
if(ans < k)
{
printf("NO\n");
return ;
} int num = ;
for(int i = ; i <= n; i++)
{
if(out[i].size() > )
{
for(int j = out[i].size() - ; j > ; j--)
{
num++;
cur[num].l = out[i][j], cur[num].r = out[i][j - ];
}
}
}
if(num > n * n)
{
printf("NO\n");
return ;
}
printf("YES\n");
printf("%d\n", num);
while()
{
int flag = ;
for(int i = ; i <= num; i++)
{
if(vis[i]) continue;
int l = cur[i].l, r = cur[i].r;
if(bel[l] == && bel[r] == )
{
printf("%d %d\n", l, r);
flag = ;
vis[i] = ;
bel[l] = , bel[r] = ;
}
}
if(!flag)
{
break;
}
}
return ;
}
/*
6 3
1 2 4
4 5 6
000100
000100
000000
000010
000001
000000
*/
Code
7. CodeForces 847J Students Initiation
当出现一条边,要么朝向左,要么朝向右,这时候不妨把它从中间拎起来,让源从这个顶点流。
当出现一个点,要么表现为1属性,要么表现为2属性,不妨把这两个属性想成一条边的两个点,把这条边中间往下沉,让这个顶点往汇流。
此题属于第一种情况。
#include <bits/stdc++.h>
using namespace std;
#define next Next
const int inf = 0x3f3f3f3f;
const int maxn=;
int level[maxn];
int iter[maxn];
int head[maxn],tot;
struct edge{
int to,cap,Next;
} e[]; ///此处应为边的两倍,加一条容量为0的反向边
void init(int n){
fill(head, head + n + , -);
tot=;
}
void add(int from,int to,int cap){
e[tot].Next=head[from];
e[tot].to=to;
e[tot].cap=cap;
head[from]=tot;
tot++;
}
void addedge(int from,int to,int cap){
add(from,to,cap);
add(to,from,);
}
void bfs(int s){
memset(level,-,sizeof(level));
queue<int> q;
level[s]=;
q.push(s);
while(!q.empty()){
int v=q.front(); q.pop();
for(int i=head[v];~i;i=e[i].Next){
edge &ed=e[i];
if(ed.cap>&&level[ed.to]<){
level[ed.to]=level[v]+;
q.push(ed.to);
}
}
}
}
int dfs(int v,int t,int f){
if(v==t) return f;
for(int &i=iter[v];~i;i=e[i].Next){
edge &ed=e[i];
if(ed.cap>&&level[v]<level[ed.to]){
int d=dfs(ed.to,t,min(f,ed.cap));
if(d>){
ed.cap-=d;
e[i^].cap+=d;
return d;
}
}
}
return ;
}
int max_flow(int s,int t){
int flow=;
while(){
bfs(s);
if(level[t]<) return flow;
memcpy(iter,head,sizeof(iter));
int f;
while((f=dfs(s,t,inf))>)
{
flow+=f;
}
}
}
int x[], y[];
int n, m;
bool check(int mid)
{
init(n + m + );
int S = m + n + , T = S + ;
for(int i = ; i <= m; i++) //每个i是边
{
addedge(i, x[i] + m, );
addedge(i, y[i] + m, );
}
for(int i = ; i <= m; i++)
{
addedge(S, i, );
}
for(int i = ; i <= n; i++)
{
addedge(i + m, T, mid);
}
return max_flow(S, T) == m;
}
struct node
{
int x, y;
}cur[];
int main()
{
scanf("%d %d", &n, &m);
for(int i = ; i <= m; i++) scanf("%d %d", &x[i], &y[i]);
int l = , r = m;
int ans = ;
while(l <= r)
{
int mid = (l + r) >> ;
if(check(mid))
{
ans = mid;
for(int i = ; i < * m; i += )
{
int tmp = i / + ;
// printf("%d %d\n", e[i].cap, e[i + 2].cap);
if(e[i].cap == ) cur[tmp].x = e[i].to - m, cur[tmp].y = e[i + ].to - m;
else cur[tmp].x = e[i + ].to - m, cur[tmp].y = e[i].to - m;
}
r = mid - ;
}
else l = mid + ;
}
if(ans <= ) return * puts("");
printf("%d\n", ans);
for(int i = ; i <= m; i++)
{
printf("%d %d\n", cur[i].x, cur[i].y);
}
return ;
}
Code
8. CodeForces 316C2 Tidying Up
实际上我觉得这题算是二分图最小权匹配。
还是先把矩阵染成黑白相间,就是$i+j$为偶数染成白色,否则染成黑色。
每个白点向周围的黑色区域连边,相同的鞋连0,不同的鞋连1。此图满足二分图的两个性质:“0元素”(白色集合内之间不会有边)和“1元素”(白色集合中的点和黑色集合中的一个点最多只会有一条边)。
我们对题目仔细分析就会发现,实际上,同样的鞋,我们最多只会移动其中一只去找另一只,而不会都动,如果都动这样只会使答案变大。
跑二分图最小权匹配后,就是匹配边中边权为0的边越多越好。匹配边中边权为0的边,就说明这条边所连的两只鞋,是同一双鞋,都不用动了。
剩下匹配边中边权为1的边,总共$n$条这样的边的话,所连的$2*n$只鞋,我们只需要移动其中的$n$只鞋去找相同的鞋就可以了。就是在这$n$只鞋所在的点中来回换。
#include <bits/stdc++.h>
using namespace std;
const int maxn=;
const int INF = 1e9;
int dist[maxn];
int pv[maxn],pe[maxn];
struct edge
{
int to, cap, pre;
int cost;
}e[];
int tot = , head[maxn];
void init(int n)
{
tot = ;
fill(head, head + n + , -);
}
void add(int from,int to,int cap,int cost)
{
e[tot].pre = head[from];
e[tot].to = to;
e[tot].cap = cap;
e[tot].cost = cost;
head[from] = tot++;
}
void addedge(int from,int to,int cap,int cost)
{
// printf("%d ", cost);
add(from,to,cap,cost);
add(to,from,,-cost);
}
int vis[maxn];
void SPFA(int s, int t)
{
for(int i = ; i < maxn; i++) dist[i] = INF;
memset(vis, , sizeof(vis));
dist[s] = , vis[s] = ;
queue<int> q;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = ;
for(int i = head[u]; ~i; i = e[i].pre)
{
int to = e[i].to, cap = e[i].cap;
if(cap > && (dist[to] - (dist[u] + e[i].cost)) > )
{
pv[to] = u, pe[to] = i;
dist[to] = dist[u] + e[i].cost;
if(!vis[to])
{
vis[to] = ;
q.push(to);
}
}
}
}
}
int min_cost_flow(int s,int t,int f,int& max_flow)
{
int ret = 0.0;
while(f>)
{
SPFA(s, t);
if(dist[t] == INF) return ret;///同一目的地,每次增广路都是最小费用
///当所有边的流量都流净后,即没有残余网络,返回。
int d = f;
for(int v=t;v!=s;v=pv[v])
{
d = min(d, e[pe[v]].cap);
}
f -= d;
max_flow += d;
ret += (int)d*dist[t]; ///走一单位就消耗dist[t]
for(int v=t;v!=s;v=pv[v])
{
e[pe[v]].cap -= d;
e[pe[v]^].cap += d;
}
}
return ret;
} int shoes[][];
int n, m;
int check(int x, int y)
{
if(x >= && x <= n && y >= && y <= m) return ;
else return ;
}
int main()
{
scanf("%d %d", &n, &m);
init(n * m + );
for(int i = ; i <= n; i++)
{
for(int j = ; j <= m; j++)
{
scanf("%d", &shoes[i][j]);
}
}
int S = n * m + , T = S + ;
for(int i = ; i <= n; i++)
{
for(int j = ; j <= m; j++)
{
// printf("%d %d\n", i, j);
int node = (i - ) * m + j;
if((i + j) % == )
{
addedge(S, node, , );
if(check(i - , j)) addedge(node, node - m, , shoes[i][j] != shoes[i - ][j]);
if(check(i + , j)) addedge(node, node + m, , shoes[i][j] != shoes[i + ][j]);
if(check(i, j - )) addedge(node, node - , , shoes[i][j] != shoes[i][j - ]);
if(check(i, j + )) addedge(node, node + , , shoes[i][j] != shoes[i][j + ]);
}
else addedge(node, T, , );
// printf("\n");
}
}
int maxflow = ;
printf("%d\n", min_cost_flow(S, T, INF, maxflow));
return ;
}
Code
9. CodeForces 1070I I. Privatization of Roads in Berland
因为最近网络流做的比较多,比赛时候一心找网络流,做完前两道题后,发现I题是网络流,(感觉已经熟悉了cf的网络流题目风格)想先做完C再去做,结果C题WA 31全程,暴露对拍了200组n≤1000的数组都没差异,泪奔……
自己独立建图,搞了一个多小时还是不知道怎么建,看了题解后,发现这种度数-限制的题目前面做过(Gym 100212I Trade)这两天要把前面做过的题目再看一遍,感觉都是很好的模型。
题解:
首先每个公司的两条边和同一个城市相连是最优的。因为如果不连同一个城市的话,一个公司两条边连接四个不同的城市,每个城市都被占了$\frac{1}{k}$的名额,还不如两条边连同一个城市,两条边才占了一个城市$\frac{1}{k}$的名额。
假如一个公司的度数为$d$,如果$d≤k$,那我们不用担心。如果$d>2\times k$,构造不出。如果$k<d≤2\times k$,那么至少有$d-k$条边与$k$条边中相同,就是至少会有$d-k$对相同的边。
那么我们可以这样建图:每条边变成一个顶点,$S$向每条边连流量为1的边,每条边分别向两个端点$u$和$v$连流量为1的边,对于每个端点,向$T$连$2\times (max(0, d-k))$流量的边,就相当于对于每个城市,去构造$d-k$对相同的边。跑最大流就可以了。
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn=;
int level[maxn];
int iter[maxn];
int head[maxn],tot;
struct edge{
int to,cap,Next;
} e[]; ///此处应为边的两倍,加一条容量为0的反向边
void init(int n)
{
fill(head, head + n + , -);
tot=;
}
void add(int from,int to,int cap){
e[tot].Next=head[from];
e[tot].to=to;
e[tot].cap=cap;
head[from]=tot;
tot++;
}
void addedge(int from,int to,int cap){
add(from,to,cap);
add(to,from,);
}
void bfs(int s){
memset(level,-,sizeof(level));
queue<int> q;
level[s]=;
q.push(s);
while(!q.empty()){
int v=q.front(); q.pop();
for(int i=head[v];~i;i=e[i].Next){
edge &ed=e[i];
if(ed.cap>&&level[ed.to]<){
level[ed.to]=level[v]+;
q.push(ed.to);
}
}
}
}
int dfs(int v,int t,int f){
if(v==t) return f;
for(int &i=iter[v];~i;i=e[i].Next){
edge &ed=e[i];
if(ed.cap>&&level[v]<level[ed.to]){
int d=dfs(ed.to,t,min(f,ed.cap));
if(d>){
ed.cap-=d;
e[i^].cap+=d;
return d;
}
}
}
return ;
}
int max_flow(int s,int t){
int flow=;
while(){
bfs(s);
if(level[t]<) return flow;
memcpy(iter,head,sizeof(iter));
int f;
while((f=dfs(s,t,inf))>){
flow+=f;
}
}
}
vector<int> cnt[];
int ans[];
int deg[];
int main()
{
int T; scanf("%d", &T);
while(T--)
{
int n, m, k; scanf("%d %d %d", &n, &m, &k);
init(n + m + );
fill(ans, ans + m + , );
fill(deg, deg + n + , );
for(int i = ; i <= n; i++) cnt[i].clear();
for(int i = ; i <= m; i++)
{
int u, v; scanf("%d %d", &u, &v);
addedge(i, u + m, );
addedge(i, v + m, );
deg[u]++, deg[v]++;
}
int S = n + m + , T = S + ;
for(int i = ; i <= m; i++) addedge(S, i, );
int num = ;
for(int i = ; i <= n; i++)
{
if(deg[i] > k)
{
addedge(i + m, T, * (deg[i] - k));
num += deg[i] - k;
}
}
num <<= ;
if(num > max_flow(S, T))
{
for(int i = ; i <= m; i++) printf("0 ");
printf("\n");
continue;
}
for(int i = ; i < * m; i += )
{
if(e[i].cap == ) cnt[e[i].to - m].push_back(i / + );
else if(e[i + ].cap == )cnt[e[i + ].to - m].push_back(i / + );
}
int flag = ;
for(int i = ; i <= n; i++)
{
for(int j = ; j < cnt[i].size(); j += )
{
flag++;
ans[cnt[i][j]] = flag;
ans[cnt[i][j + ]] = flag;
}
}
for(int i = ; i <= m; i++)
{
if(ans[i]) printf("%d ", ans[i]);
else printf("%d ", ++flag);
}
printf("\n");
}
return ;
}
Code
10 Gym 101190D Delight for a Cat
做到这题,我发现我必须总结一下这种题型了。
Gym 101736D Dessert First Strategy
Codeforces 最大流 费用流的更多相关文章
- 【BZOJ】1834: [ZJOI2010]network 网络扩容(最大流+费用流)
http://www.lydsy.com/JudgeOnline/problem.php?id=1834 我又思考人生了T_T,nd的数组开小了,一直wa,调了一个小时才发现啊!!!!!我一直以为我的 ...
- 2018.10.13 bzoj1834: [ZJOI2010]network 网络扩容(最大流+费用流)
传送门 网络流水题啊. 第一问直接放心跑最大流(本来还以为有什么tricktricktrick). 第二问就直接把原来的边(u,v,c,w)(u,v,c,w)(u,v,c,w)变成(u,v,c,0)( ...
- BZOJ 1834--网络扩容(最大流&费用流)
1834: [ZJOI2010]network 网络扩容 Time Limit: 3 Sec Memory Limit: 64 MBSubmit: 3351 Solved: 1750[Submit ...
- bzoj 1834: [ZJOI2010]network 网络扩容 -- 最大流+费用流
1834: [ZJOI2010]network 网络扩容 Time Limit: 3 Sec Memory Limit: 64 MB Description 给定一张有向图,每条边都有一个容量C和一 ...
- BZOJ 1834 网络扩容(最大流+费用流)
对于第一问,直接求最大流. 对于第二问,建源点s和汇点t,s连1容量为INF,费用为0的边,n连t容量为最大流+k,费用为0的边.这样就把最大流限制为最多增加k了. 限制需要求扩充的最小费用,原图的边 ...
- 【最大流/费用流】BZOJ1834-[ZJOI2010]network 网络扩容
[题目大意] 给定一张有向图,每条边都有一个容量C和一个扩容费用W.这里扩容费用是指将容量扩大1所需的费用.求: 1. 在不扩容的情况下,1到N的最大流: 2. 将1到N的最大流增加K所需的最小扩容费 ...
- HDU 2485 Destroying the bus stations(!最大流∩!费用流∩搜索)
Description Gabiluso is one of the greatest spies in his country. Now he’s trying to complete an “im ...
- [BZOJ1834][ZJOI2010]network 网络扩容 最大流+费用流
1834: [ZJOI2010]network 网络扩容 Time Limit: 3 Sec Memory Limit: 64 MB Submit: 3330 Solved: 1739 [Subm ...
- 【费用流】【CODEVS】1227 方格取数2
[算法]最小费用最大流(费用流) [题解] 费用流:http://www.cnblogs.com/onioncyc/p/6496532.html 本题构图: 在有限的k次行走中尽可能多的拿到数字,明显 ...
随机推荐
- [原]sencha touch之布局
今天记录一下关于sencha touch中的几种布局,其实很简单的,还是直接上代码,一目了然 1:box布局,其实就是vbox和hbox,说白了一个是横着摆放,一个是竖着摆放 Ext.applicat ...
- RxJava Rxandroid retrofit
其实Retrofit会了.集合RxJava,RxAndroid 就很简单了. 只需要改几个地方. 1.接口里面返回的对象不再是 call,而是Observable public interface A ...
- JAVA后端常用框架SSM,redis,dubbo等
JAVA后端常用框架SSM,redis,dubbo等 一.SpringMVC http://blog.csdn.net/evankaka/article/details/45501811 spri ...
- Excel动画教程50例(二)
Excel动画教程50例(二) 16.用好Excel的“搜索函数” 17.在Excel中插入超级链接 18.在Excel中打印指定页面 19.在Excel中直接编辑“宏” 20.用窗体调用“宏” 21 ...
- 【Partition List】cpp
题目: Given a linked list and a value x, partition it such that all nodes less than x come before node ...
- plsql 编程基础
分支 declare --声明变量 a ); b ); c ); begin --开始 a := '小明'; dbms_output.put_line(a); b :; c :; if b > ...
- mysql安装 以及跳过密码登录重设
修改MySQL的登录设置: vi /etc/my.cnf 在[mysqld]的段中加上一句:skip-grant-tables 例如: [mysqld] datadir=/var/lib/mysql ...
- [python][django学习篇][8]django 视图(2) --简单模板
在视图函数里返回的是一个 HttpResponse 类的实例,我们给它传入了一个希望显示在用户浏览器上的字符串.但是我们的博客不可能只显示这么一句话,它有可能会显示很长很长的内容.比如我们发布的博客文 ...
- 直接选择排序(java)
直接选择排序的逻辑非常简单,数组{A1.......An} 先在{A1........An}去获取最小的 与A1交换位置 然后在{A2..........An}中取出最小的 与A2交换位置. ...
- 【bzoj3329】Xorequ 数位dp+矩阵乘法
题目描述 输入 第一行一个正整数,表示数据组数据 ,接下来T行每行一个正整数N 输出 2*T行第2*i-1行表示第i个数据中问题一的解, 第2*i行表示第i个数据中问题二的解, 样例输入 1 1 样例 ...