Codeforces 最大流 费用流

这套题目做完后,一定要反复的看!

代码经常出现的几个问题:

本机测试超时:

1.init函数忘记写。

2.addedge函数写成add函数。

3.边连错了。

代码TLE:

1.前向星边数组开小.

2.用了memset,慎用。

 

1. CodeForces 498C  Array and Operations

 我发现cf上的网络流的建图思路都非常好,准备着重练习一下。

 此题枚举每一个质因子,对每个质因子建图,确实是很好的思路。

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 
  4 #define next Next
  5 const int inf = 0x3f3f3f3f;
  6 const int maxn=205;
  7 int level[maxn];
  8 int iter[maxn];
  9 int head[maxn],tot;
 10 struct edge{
 11     int to,cap,Next;
 12 } e[5500]; ///此处应为边的两倍,加一条容量为0的反向边
 13 void init(int n){
 14 
 15     for(int i = 0; i <= n; i++) head[i] = -1;
 16     tot=0;
 17 }
 18 void add(int from,int to,int cap){
 19     e[tot].Next=head[from];
 20     e[tot].to=to;
 21     e[tot].cap=cap;
 22     head[from]=tot;
 23     tot++;
 24 }
 25 void addedge(int from,int to,int cap){
 26     add(from,to,cap);
 27     add(to,from,0);
 28 }
 29 void bfs(int s){
 30     memset(level,-1,sizeof(level));
 31     queue<int> q;
 32     level[s]=0;
 33     q.push(s);
 34     while(!q.empty()){
 35         int v=q.front(); q.pop();
 36         for(int i=head[v];~i;i=e[i].Next){
 37             edge &ed=e[i];
 38             if(ed.cap>0&&level[ed.to]<0){
 39                 level[ed.to]=level[v]+1;
 40                 q.push(ed.to);
 41             }
 42         }
 43     }
 44 }
 45 int dfs(int v,int t,int f){
 46     if(v==t) return f;
 47     for(int &i=iter[v];~i;i=e[i].Next){
 48         edge &ed=e[i];
 49         if(ed.cap>0&&level[v]<level[ed.to]){
 50             int d=dfs(ed.to,t,min(f,ed.cap));
 51             if(d>0){
 52                 ed.cap-=d;
 53                 e[i^1].cap+=d;
 54                 return d;
 55             }
 56         }
 57     }
 58     return 0;
 59 }
 60 int max_flow(int s,int t){
 61     int flow=0;
 62     while(1){
 63         bfs(s);
 64         if(level[t]<0) return flow;
 65         memcpy(iter,head,sizeof(iter));
 66         int f;
 67         while((f=dfs(s,t,inf))>0){
 68             flow+=f;
 69         }
 70     }
 71 }
 72 
 73 //线性素数筛
 74 const int N = 1e5 + 50;
 75 int prime[N], num_prime = 0;
 76 int isNotPrime[N];
 77 void is_prime(int N)
 78 {
 79     isNotPrime[0] = isNotPrime[1] = 1;
 80     for(int i=2;i<N;i++)
 81     {
 82         if(!isNotPrime[i])
 83         {
 84             prime[num_prime++] = i;
 85         }
 86         for(int j=0;j<num_prime&&i*prime[j]<N;j++)
 87         {
 88             isNotPrime[i*prime[j]] = 1;
 89             if(!(i%prime[j]))
 90             {
 91                 break;
 92             }
 93         }
 94     }
 95     return;
 96 }
 97 
 98 int a[111];
 99 vector<int> p[111];
100 vector<int> cnt[111];
101 int l[111][2];
102 vector<int> big; ///大素数
103 int n, m;
104 int solve(int ans)
105 {
106     init(n + 1);
107     int s = 0, t = n + 1;
108     for(int j = 1; j <= n; j++)
109     {
110         int pos = lower_bound(p[j].begin(), p[j].end(), ans) - p[j].begin();
111         if(pos < p[j].size() && p[j][pos] == ans)
112         {
113            // printf("%d %d %d\n", j, p[j][pos], cnt[j][pos]);
114             if(j & 1) addedge(s, j, cnt[j][pos]);
115             else addedge(j, t, cnt[j][pos]);
116         }
117     }
118     for(int j = 1; j <= m; j++)
119     {
120         int tmp = 100;
121         for(int k = 0; k < 2; k++)
122         {
123            // printf("%d\n", l[j][k]);
124             int pos = lower_bound(p[l[j][k]].begin(), p[l[j][k]].end(), ans) - p[l[j][k]].begin();
125           //  printf("%d  %d  %d\n", j, pos, cnt[l[j][k]][pos]);
126             if(pos < p[l[j][k]].size() && p[l[j][k]][pos] == ans)
127             {
128                tmp = min(tmp, cnt[l[j][k]][pos]);
129             }
130             else tmp = 0;
131         }
132        // printf("%d %d %d\n", l[j][0], l[j][1], tmp);
133         addedge(l[j][0], l[j][1], tmp);
134     }
135    // return 0;
136     return max_flow(s, t);
137 }
138 int main()
139 {
140     is_prime(1e5 + 5);
141     scanf("%d %d", &n, &m);
142     for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
143     for(int i = 1; i <= n; i++)
144     {
145         for(int j = 2; j <= 1e5; j++)
146         {
147             if(j > a[i]) break;
148             if(a[i] % j == 0) p[i].push_back(j);
149             int tmp = 0;
150             while(a[i] % j == 0)
151             {
152                 tmp++;
153                 a[i] /= j;
154             }
155             if(tmp) cnt[i].push_back(tmp);
156         }
157         if(a[i] > 1) p[i].push_back(a[i]), cnt[i].push_back(1);
158     }
159     for(int i = 1; i <= m; i++)
160     {
161         int go1, go2; scanf("%d %d", &go1, &go2);
162         if(go1 % 2 == 0) swap(go1, go2);
163         l[i][0] = go1, l[i][1] = go2;
164     }
165     int ans = 0;
166     for(int i = 0; i < num_prime; i++)
167     {
168         ans += solve(prime[i]);
169 
170     }
171     for(int i = 1; i <= n; i++)
172     {
173         int t = p[i].size();
174         if(t == 0) continue;
175         if(p[i][t - 1] >= 1e5) big.push_back(p[i][t - 1]);
176     }
177     sort(big.begin(), big.end());
178     big.erase(unique(big.begin(), big.end()), big.end());
179     for(int i = 0; i < (int)big.size(); i++)
180     {
181         ans += solve(big[i]);
182     }
183     printf("%d\n", ans);
184     return 0;
185 }
186 /*
187 3 2
188 6 12 8
189 1 2
190 2 3
191 */
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 = 200;
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,0,-cost,g[from].size()-1));
}
int vis[maxn];
void SPFA(int s, int t)
{
    for(int i = 1; i < maxn; i++) dist[i] = INF;
    memset(vis, 0, sizeof(vis));
    dist[s] = 0, vis[s] = 1;
    queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = 0; i < g[u].size(); i++)
        {
            edge &e = g[u][i];
            if(e.cap > 0 && (dist[e.to] - (dist[u] + e.cost)) > 0)
            {
                pv[e.to] = u, pe[e.to] = i;
                dist[e.to] = dist[u] + e.cost;
                if(!vis[e.to])
                {
                    vis[e.to] = 1;
                    q.push(e.to);
                }
            }
        }
    }
}
int min_cost_flow(int s,int t,int f,int& max_flow)
{
    int ret = 0.0;
    while(f>0)
    {
        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[111];
int b[111];
int cnt[30];
int main()
{

    int n; scanf("%d", &n);
    scanf("%s", s + 1);
    for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
    for(int i = 1; i <= n; i++)
    {
        cnt[s[i] - 'a' + 1]++;
    }
    int S = 26 + n / 2 + 1, T = S + 1;
    for(int i = 1; i <= 26; i++)
    {
        addedge(S, i, cnt[i], 0);
     //   printf("%c %d\n", i + 'a' - 1, cnt[i]);
        if(cnt[i])
        {
            for(int j = 1; j <= n / 2; j++)
            {
                char c = i + 'a' - 1;
                int flag = 0;
                if(s[j] == c) flag++;
                if(s[n - j + 1] == c) flag++;
                if(!flag) addedge(i, 26 + j, 1, 0);
                else if(flag == 1)
                {
                    if(s[j] == c) addedge(i, 26 + j, 1, -b[j]);
                    else addedge(i, 26 + j, 1, -b[n - j + 1]);
                }
                else
                {
                    addedge(i, 26 + j, 1, min(-b[j], -b[n - j + 1]));
                }
            }
        }
    }
    for(int j = 1; j <= n / 2; j++)
    {
        addedge(j + 26, T, 2, 0);
    }
    int maxflow = 0;
    int ans = min_cost_flow(S, T, INF, maxflow);
    printf("%d\n", -ans);
    return 0;
}
Code

 

3. CodeForces 164C Machine Programming

算了好几次head大小愣是算不对,这题前面出过好几次了。 焦作F

这个输出路径还是用前向星好,每条边都有索引,方便判断$e.cap$是否用完。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3000;
const int INF = 1e9;
int dist[maxn];
int pv[maxn],pe[maxn];
struct edge
{
    int to, cap, pre;
    int cost;
}e[7000];
int tot = 0, head[maxn];
void init(int n)
{
    tot = 0;
    fill(head, head + n + 1, -1);
}
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,0,-cost);
}
int vis[maxn];
void SPFA(int s, int t)
{
    for(int i = 0; i < maxn; i++) dist[i] = INF;
    memset(vis, 0, sizeof(vis));
    dist[s] = 0, vis[s] = 1;
    queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = head[u]; ~i; i = e[i].pre)
        {
            int to = e[i].to, cap = e[i].cap;
            if(cap > 0 && (dist[to] - (dist[u] + e[i].cost)) > 0)
            {
                pv[to] = u, pe[to] = i;
                dist[to] = dist[u] + e[i].cost;
                if(!vis[to])
                {
                    vis[to] = 1;
                    q.push(to);
                }
            }
        }
    }
}
int min_cost_flow(int s,int t,int f,int& max_flow)
{
    int ret = 0.0;
    while(f>0)
    {
        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]^1].cap += d;
        }
    }
    return ret;
}
int s[1111], t[1111], c[1111], x[2222];
int main()
{
    init(2900);
    int n, k; scanf("%d %d", &n, &k);
    int cnt = 0;
    for(int i = 1; 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 = 1; 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 + 2, T = S + 3;
    for(int i = 1; i <= n; i++)
    {
        addedge(s[i], t[i], 1, -c[i]);
    }
    addedge(S, 0, k, 0);
    for(int i = 0; i < cnt; i++)
    {
        addedge(i, i + 1, k, 0);
    }
    addedge(cnt, T, k, 0);
    int maxflow = 0;
    min_cost_flow(S, T, INF, maxflow);
    for(int i = 0; i < 2 * n; i += 2)
    {
        if(e[i].cap == 0) printf("1 ");
        else printf("0 ");
    }
    return 0;
}
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=705;
int level[maxn];
int iter[maxn];
int head[maxn],tot;
struct edge{
    int to,cap,Next;
} e[200000]; ///此处应为边的两倍,加一条容量为0的反向边
void init(int n){
    fill(head, head + n + 1, -1);
    tot=0;
}
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,0);
}
void bfs(int s){
    memset(level,-1,sizeof(level));
    queue<int> q;
    level[s]=0;
    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>0&&level[ed.to]<0){
                level[ed.to]=level[v]+1;
                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>0&&level[v]<level[ed.to]){
            int d=dfs(ed.to,t,min(f,ed.cap));
            if(d>0){
                ed.cap-=d;
                e[i^1].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s,int t){
    int flow=0;
    while(1){
        bfs(s);
        if(level[t]<0) return flow;
        memcpy(iter,head,sizeof(iter));
        int f;
        while((f=dfs(s,t,inf))>0){
            flow+=f;
        }
    }
}

int u[90005], v[90005];
int deu[305], dev[300];
int del[90005];
int main()
{
    freopen("trade.in", "r", stdin);
    freopen("trade.out", "w", stdout);
    init(701);
    int n, m, k; scanf("%d %d %d", &n, &m, &k);
    for(int i = 1; i <= k; i++)
    {
        scanf("%d %d", &u[i], &v[i]);
        deu[u[i]]++, dev[v[i]]++;
    }
    for(int i = 1; i <= n; i++)
    {
        if(deu[i] < 2)
        {
            printf("-1\n");
            return 0;
        }
    }
    for(int i = 1; i <= m; i++)
    {
        if(dev[i] < 2)
        {
            printf("-1\n");
            return 0;
        }
    }
    for(int i = 1; i <= k; i++)
    {
        addedge(u[i], v[i] + n, 1);
    }
    int S = n + m + 1, T = n + m + 2;
    for(int i = 1; i <= n; i++)
    {
        if(deu[i] >= 2) addedge(S, i, deu[i] - 2);
    }
    for(int i = 1; i <= m; i++)
    {
        if(dev[i] >= 2) addedge(i + n, T, dev[i] - 2);
    }
    int ans = max_flow(S, T);
    printf("%d\n", k - ans);
    for(int i = 0; i < 2 * k; i += 2)
    {
        if(e[i].cap == 0) del[i / 2 + 1] = 1;
    }
    for(int i = 1; i <= k; i++)
    {
        if(!del[i]) printf("%d ", i);
    }
    return 0;
}
Code

 

5. CodeForces 730I  Olympiad in Programming and Sports

怎么保证每个点只被用一次?

从p和s连向它,再引出一条流量为1的边。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3100;
const int INF = 1e9;
int dist[maxn];
int pv[maxn],pe[maxn];
struct edge
{
    int to, cap, pre;
    int cost;
}e[20000];
int tot = 0, head[maxn];
void init(int n)
{
    tot = 0;
    fill(head, head + n + 1, -1);
}
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,0,-cost);
}
int vis[maxn];
void SPFA(int s, int t)
{
    for(int i = 1; i < maxn; i++) dist[i] = INF;
    memset(vis, 0, sizeof(vis));
    dist[s] = 0, vis[s] = 1;
    queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = head[u]; ~i; i = e[i].pre)
        {
            int to = e[i].to, cap = e[i].cap;
            if(cap > 0 && (dist[to] - (dist[u] + e[i].cost)) > 0)
            {
                pv[to] = u, pe[to] = i;
                dist[to] = dist[u] + e[i].cost;
                if(!vis[to])
                {
                    vis[to] = 1;
                    q.push(to);
                }
            }
        }
    }
}
int min_cost_flow(int s,int t,int f,int& max_flow)
{
    int ret = 0;
    while(f>0)
    {
        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]^1].cap += d;
        }
    }
    return ret;
}

int main()
{

    int n, p, s; scanf("%d %d %d", &n, &p, &s);
    init(n + 4);
    int l = n + 1, r = n + 2, S = n + 3, T = S + 1;
    for(int i = 1; i <= n; i++)
    {
        int a; scanf("%d", &a);
        addedge(l, i, 1, -a);
    }
    for(int i = 1; i <= n; i++)
    {
        int b; scanf("%d", &b);
        addedge(r, i, 1, -b);
    }
    addedge(S, l, p, 0), addedge(S, r, s, 0);
    for(int i = 1; i <= n; i++)
    {
        addedge(i, T, 1, 0);
    }
    int maxflow = 0;
    int ans = min_cost_flow(S, T, INF, maxflow);
    printf("%d\n", -ans);
    for(int i = 0; i < 2 * n; i += 2)
    {
        if(e[i].cap == 0) printf("%d ", i / 2 + 1);
    }
    printf("\n");
    for(int i = 2 * n; i < 4 * n; i += 2)
    {
        if(e[i].cap == 0) printf("%d ", i / 2 - n + 1);
    }
}
Code

 

 6. Gym 101755D Transfer Window

很开心,接连几道题自己可以独立建图,并且成功AC,说明没有白训练,做 -> 受益 -> 做,形成闭环。

 这道题图不是很难建,但是要输出网络流的路径,这就要求对最大流模板很熟悉,改模板。

#include <bits/stdc++.h>
using namespace std;
#define next Next
const int inf = 0x3f3f3f3f;
const int maxn=400;
int level[maxn];
int iter[maxn];
int head[maxn],tot;
int pv[maxn],pe[maxn];
struct edge{
    int to,cap,Next;
} e[200000]; ///此处应为边的两倍,加一条容量为0的反向边
void init(int n){
    fill(head, head + n + 1, -1);
    tot=0;
}
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,0);
}
void bfs(int s){
    memset(level,-1,sizeof(level));
    queue<int> q;
    level[s]=0;
    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>0&&level[ed.to]<0){
                level[ed.to]=level[v]+1;
                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>0&&level[v]<level[ed.to]){
            int d=dfs(ed.to,t,min(f,ed.cap));
            if(d>0){
                pv[ed.to] = v, pe[ed.to] = i;
                ed.cap-=d;
                e[i^1].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
vector<int> out[maxn];
int max_flow(int s,int t){
    int flow=0;
    while(1){
        bfs(s);
        if(level[t]<0) return flow;
        memcpy(iter,head,sizeof(iter));
        int f;
        while((f=dfs(s,t,inf))>0)
        {
            int last = 0;
            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[90005];
int bel[305];
int vis[90005];
int main()
{
    int n, k; scanf("%d %d", &n, &k);
    init(n + 2);
    int S = n + 1, T = S + 1;
    for(int i = 1; i <= k; i++)
    {
        int a; scanf("%d", &a);
        addedge(S, a, 1);
        bel[a]++;
    }
    for(int i = 1; i <= k; i++)
    {
        int a; scanf("%d", &a);
        addedge(a, T, 1);
    }
    for(int i = 1; i <= n; i++)
    {
        scanf("%s", s[i] + 1);
        for(int j = 1; j <= n; j++)
        {
            if(s[i][j] == '1')
            {
                addedge(i, j, inf);
            }
        }
    }
    int ans = max_flow(S, T);
    if(ans < k)
    {
        printf("NO\n");
        return 0;
    }

    int num = 0;
    for(int i = 1; i <= n; i++)
    {
        if(out[i].size() > 1)
        {
            for(int j = out[i].size() - 1; j > 0; j--)
            {
                num++;
                cur[num].l = out[i][j], cur[num].r = out[i][j - 1];
            }
        }
    }
    if(num > n * n)
    {
        printf("NO\n");
        return 0;
    }
    printf("YES\n");
    printf("%d\n", num);
    while(1)
    {
        int flag = 0;
        for(int i = 1; i <= num; i++)
        {
            if(vis[i]) continue;
            int l = cur[i].l, r = cur[i].r;
            if(bel[l] == 1 && bel[r] == 0)
            {
                printf("%d %d\n", l, r);
                flag = 1;
                vis[i] = 1;
                bel[l] = 0, bel[r] = 1;
            }
        }
        if(!flag)
        {
            break;
        }
    }
    return 0;
}
/*
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=11000;
int level[maxn];
int iter[maxn];
int head[maxn],tot;
struct edge{
    int to,cap,Next;
} e[50000]; ///此处应为边的两倍,加一条容量为0的反向边
void init(int n){
    fill(head, head + n + 1, -1);
    tot=0;
}
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,0);
}
void bfs(int s){
    memset(level,-1,sizeof(level));
    queue<int> q;
    level[s]=0;
    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>0&&level[ed.to]<0){
                level[ed.to]=level[v]+1;
                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>0&&level[v]<level[ed.to]){
            int d=dfs(ed.to,t,min(f,ed.cap));
            if(d>0){
                ed.cap-=d;
                e[i^1].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s,int t){
    int flow=0;
    while(1){
        bfs(s);
        if(level[t]<0) return flow;
        memcpy(iter,head,sizeof(iter));
        int f;
        while((f=dfs(s,t,inf))>0)
        {
            flow+=f;
        }
    }
}
int x[5005], y[5005];
int n, m;
bool check(int mid)
{
    init(n + m + 2);
    int S = m + n + 1, T = S + 1;
    for(int i = 1; i <= m; i++) //每个i是边
    {
        addedge(i, x[i] + m, 1);
        addedge(i, y[i] + m, 1);
    }
    for(int i = 1; i <= m; i++)
    {
        addedge(S, i, 1);
    }
    for(int i = 1; i <= n; i++)
    {
        addedge(i + m, T, mid);
    }
    return max_flow(S, T) == m;
}
struct node
{
    int x, y;
}cur[5005];
int main()
{
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= m; i++) scanf("%d %d", &x[i], &y[i]);
    int l = 1, r = m;
    int ans = 0;
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        if(check(mid))
        {
            ans = mid;
            for(int i = 0; i < 4 * m; i += 4)
            {
                int tmp = i / 4 + 1;
               // printf("%d %d\n", e[i].cap, e[i + 2].cap);
                if(e[i].cap == 0) cur[tmp].x = e[i].to - m, cur[tmp].y = e[i + 2].to - m;
                else cur[tmp].x = e[i + 2].to - m, cur[tmp].y = e[i].to - m;
            }
            r = mid - 1;
        }
        else l = mid + 1;
    }
    if(ans <= 0) return 0 * puts("0");
    printf("%d\n", ans);
    for(int i = 1; i <= m; i++)
    {
        printf("%d %d\n", cur[i].x, cur[i].y);
    }
    return 0;
}
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=7000;
const int INF = 1e9;
int dist[maxn];
int pv[maxn],pe[maxn];
struct edge
{
    int to, cap, pre;
    int cost;
}e[40000];
int tot = 0, head[maxn];
void init(int n)
{
    tot = 0;
    fill(head, head + n + 1, -1);
}
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,0,-cost);
}
int vis[maxn];
void SPFA(int s, int t)
{
    for(int i = 1; i < maxn; i++) dist[i] = INF;
    memset(vis, 0, sizeof(vis));
    dist[s] = 0, vis[s] = 1;
    queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = head[u]; ~i; i = e[i].pre)
        {
            int to = e[i].to, cap = e[i].cap;
            if(cap > 0 && (dist[to] - (dist[u] + e[i].cost)) > 0)
            {
                pv[to] = u, pe[to] = i;
                dist[to] = dist[u] + e[i].cost;
                if(!vis[to])
                {
                    vis[to] = 1;
                    q.push(to);
                }
            }
        }
    }
}
int min_cost_flow(int s,int t,int f,int& max_flow)
{
    int ret = 0.0;
    while(f>0)
    {
        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]^1].cap += d;
        }
    }
    return ret;
}

int shoes[88][88];
int n, m;
int check(int x, int y)
{
    if(x >= 1 && x <= n && y >= 1 && y <= m) return 1;
    else return 0;
}
int main()
{
    scanf("%d %d", &n, &m);
    init(n * m + 5);
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            scanf("%d", &shoes[i][j]);
        }
    }
    int S = n * m + 1, T = S + 1;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
           // printf("%d %d\n", i, j);
            int node = (i - 1) * m + j;
            if((i + j) % 2 == 0)
            {
                addedge(S, node, 1, 0);
                if(check(i - 1, j)) addedge(node, node - m, 1, shoes[i][j] != shoes[i - 1][j]);
                if(check(i + 1, j)) addedge(node, node + m, 1, shoes[i][j] != shoes[i + 1][j]);
                if(check(i, j - 1)) addedge(node, node - 1, 1, shoes[i][j] != shoes[i][j - 1]);
                if(check(i, j + 1)) addedge(node, node + 1, 1, shoes[i][j] != shoes[i][j + 1]);
            }
            else addedge(node, T, 1, 0);
          //  printf("\n");
        }
    }
    int maxflow = 0;
    printf("%d\n", min_cost_flow(S, T, INF, maxflow));
    return 0;
}
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=1300;
int level[maxn];
int iter[maxn];
int head[maxn],tot;
struct edge{
    int to,cap,Next;
} e[5000]; ///此处应为边的两倍,加一条容量为0的反向边
void init(int n)
{
    fill(head, head + n + 1, -1);
    tot=0;
}
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,0);
}
void bfs(int s){
    memset(level,-1,sizeof(level));
    queue<int> q;
    level[s]=0;
    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>0&&level[ed.to]<0){
                level[ed.to]=level[v]+1;
                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>0&&level[v]<level[ed.to]){
            int d=dfs(ed.to,t,min(f,ed.cap));
            if(d>0){
                ed.cap-=d;
                e[i^1].cap+=d;
                return d;
            }
        }
    }
    return 0;
}
int max_flow(int s,int t){
    int flow=0;
    while(1){
        bfs(s);
        if(level[t]<0) return flow;
        memcpy(iter,head,sizeof(iter));
        int f;
        while((f=dfs(s,t,inf))>0){
            flow+=f;
        }
    }
}
vector<int> cnt[700];
int ans[700];
int deg[700];
int main()
{
    int T; scanf("%d", &T);
    while(T--)
    {
        int n, m, k; scanf("%d %d %d", &n, &m, &k);
        init(n + m + 5);
        fill(ans, ans + m + 1, 0);
        fill(deg, deg + n + 1, 0);
        for(int i = 1; i <= n; i++) cnt[i].clear();
        for(int i = 1; i <= m; i++)
        {
            int u, v; scanf("%d %d", &u, &v);
            addedge(i, u + m, 1);
            addedge(i, v + m, 1);
            deg[u]++, deg[v]++;
        }
        int S = n + m + 1, T = S + 1;
        for(int i = 1; i <= m; i++) addedge(S, i, 1);
        int num = 0;
        for(int i = 1; i <= n; i++)
        {
            if(deg[i] > k)
            {
                addedge(i + m, T, 2 * (deg[i] - k));
                num += deg[i] - k;
            }
        }
        num <<= 1;
        if(num > max_flow(S, T))
        {
            for(int i = 1; i <= m; i++) printf("0 ");
            printf("\n");
            continue;
        }
        for(int i = 0; i < 4 * m; i += 4)
        {
            if(e[i].cap == 0) cnt[e[i].to - m].push_back(i / 4 + 1);
            else if(e[i + 2].cap == 0)cnt[e[i + 2].to - m].push_back(i / 4 + 1);
        }
        int flag = 0;
        for(int i = 1; i <= n; i++)
        {
            for(int j = 0; j < cnt[i].size(); j += 2)
            {
                flag++;
                ans[cnt[i][j]] = flag;
                ans[cnt[i][j + 1]] = flag;
            }
        }
        for(int i = 1; i <= m; i++)
        {
            if(ans[i]) printf("%d ", ans[i]);
            else printf("%d ", ++flag);
        }
        printf("\n");
    }
    return 0;
}
Code

 

10 Gym 101190D Delight for a Cat

做到这题,我发现我必须总结一下这种题型了。

 链接

Gym 101736D Dessert First Strategy

 

posted @ 2018-10-17 10:26  汪汪鱼  阅读(444)  评论(0编辑  收藏  举报