网络流入门专题(洛谷题单)

网络流入门到入土(#1)

最大流:

裸最大流:

P1343 地震逃生

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
//const int maxm = 1e3 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int m,n,s,t, x, head[50000], num_edge;
int cur[50000],deep[50000],last[50000],num[50000];
//cur当前弧优化; last该点的上一条边; num桶 用来GAP优化
struct Edge{
    int next,to,dis;
}edge[400000];
void add_edge(int from,int to,int dis)
{
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    edge[num_edge].next=head[from];
    head[from]=num_edge++;
}
//bfs仅用于更新deep
void bfs(int t)
{
    queue<int>q;
    for (int i=0; i<=n; i++) cur[i]=head[i];
    for (int i=1; i<=n; i++) deep[i]=n;
    deep[t]=0;
    q.push(t);
    while (!q.empty())
    {
        int now=q.front(); q.pop();
        for (int i=head[now]; i != -1; i=edge[i].next)
        {
            if (deep[edge[i].to]==n && edge[i^1].dis)//i^1是为了找反边
            {
                deep[edge[i].to] = deep[now]+1;
                q.push(edge[i].to);
            }
        }
    }
}

int add_flow(int s,int t)
{
    int ans=inf,now=t;
    while (now!=s)
    {
        ans=min(ans,edge[last[now]].dis);
        now=edge[last[now]^1].to;
    }
    now=t;
    while (now!=s)
    {
        edge[last[now]].dis-=ans;
        edge[last[now]^1].dis+=ans;
        now=edge[last[now]^1].to;
    }
    return ans;
}
int isap(int s,int t)
{
    int now=s;
    int maxflow = 0;
    bfs(t);//搜出一条增广路
    for (int i=1; i<=n; i++) num[deep[i]]++;
    while (deep[s]<n)
    {
        if (now==t)
        {//如果到达汇点就直接增广,重新回到源点进行下一轮增广
            maxflow+=add_flow(s,t);
            now=s;//回到源点
        }
        bool has_find=0;
        for (int i=cur[now]; i!=-1; i=edge[i].next)
        {
            if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路
            {
                has_find=true;
                cur[now]=i;//当前弧优化
                now=edge[i].to;
                last[edge[i].to]=i;
                break;
            }
        }
        if (!has_find)//没有找到出边,重新编号
        {
            int minn=n-1;
            for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径
                if (edge[i].dis)
                    minn=min(minn,deep[edge[i].to]);
            if ((--num[deep[now]])==0) break;//GAP优化 出现了断层
            num[deep[now]=minn+1]++;
            cur[now]=head[now];
            if (now!=s)
                now=edge[last[now]^1].to;
        }
    }
    return maxflow;
}
int main(){
    num_edge = 0;
    memset(head,-1,sizeof(head));
    scanf("%d%d%d",&n,&m,&x);
    for(int i=0;i<m;i++){
            int u,v,cap;
            scanf("%d%d%d",&u,&v,&cap);
            add_edge(u,v,cap);
            add_edge(v,u,0);
    }
    s = 1, t = n;
    int ans = isap(s,t);
    if (!ans) cout << "Orz Ni Jinan Saint Cow!" << endl;
    else {
        int ans2 = ceil(x*1./ans);
        printf("%d %d\n", ans, ans2);
    }

    return 0;
}
View Code

P2740 [USACO4.2]草地排水

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
//const int maxm = 1e3 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int m,n,s,t, x, head[maxn], num_edge;
int cur[maxn],deep[maxn],last[maxn],num[maxn];
int node_num;
//cur当前弧优化; last该点的上一条边; num桶 用来GAP优化
struct Edge{
    int next,to,dis;
}edge[maxn*2];

void add_edge(int from,int to,int dis)
{
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    edge[num_edge].next=head[from];
    head[from]=num_edge++;
}
//bfs仅用于更新deep
void bfs(int t)
{
    queue<int>q;
    for (int i=0; i<=node_num; i++) cur[i]=head[i];
    for (int i=1; i<=node_num; i++) deep[i]=node_num;
    deep[t]=0;
    q.push(t);
    while (!q.empty())
    {
        int now=q.front(); q.pop();
        for (int i=head[now]; i != -1; i=edge[i].next)
        {
            if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边
            {
                deep[edge[i].to] = deep[now]+1;
                q.push(edge[i].to);
            }
        }
    }
}

int add_flow(int s,int t)
{
    int ans=inf,now=t;
    while (now!=s)
    {
        ans=min(ans,edge[last[now]].dis);
        now=edge[last[now]^1].to;
    }
    now=t;
    while (now!=s)
    {
        edge[last[now]].dis-=ans;
        edge[last[now]^1].dis+=ans;
        now=edge[last[now]^1].to;
    }
    return ans;
}
int isap(int s,int t)
{
    int now=s;
    int maxflow = 0;
    bfs(t);//搜出一条增广路
    for (int i=1; i<=node_num; i++) num[deep[i]]++;
    while (deep[s]<node_num)
    {
        if (now==t)
        {//如果到达汇点就直接增广,重新回到源点进行下一轮增广
            maxflow+=add_flow(s,t);
            now=s;//回到源点
        }
        bool has_find=0;
        for (int i=cur[now]; i!=-1; i=edge[i].next)
        {
            if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路
            {
                has_find=true;
                cur[now]=i;//当前弧优化
                now=edge[i].to;
                last[edge[i].to]=i;
                break;
            }
        }
        if (!has_find)//没有找到出边,重新编号
        {
            int minn=node_num-1;
            for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径
                if (edge[i].dis)
                    minn=min(minn,deep[edge[i].to]);
            if ((--num[deep[now]])==0) break;//GAP优化 出现了断层
            num[deep[now]=minn+1]++;
            cur[now]=head[now];
            if (now!=s)
                now=edge[last[now]^1].to;
        }
    }
    return maxflow;
}
int main(){
    num_edge = 0;
    memset(head,-1,sizeof(head));

    scanf("%d %d", &n, &m);
    node_num = m;
    s = 1, t = m;
    for (int i = 1, u, v, w; i <= n; ++ i) {
        scanf("%d %d %d", &u, &v, &w);
        add_edge(u, v, w), add_edge(v, u, 0);
    }

    int ans = isap(s,t);
    printf("%d\n", ans);

    return 0;
}
View Code

SP4063 MPIGS - Sell Pigs

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, S, T;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline int read() {
    char c = getchar(); int x = 0, f = 1;
    for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}

inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline int dfs(int u, int flow){
    if(u == T) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

int cap[maxn];
int vis[maxn];
int main() {
    init();
    scanf("%d %d", &m, &n);
    S = 0, T = n+1;
    node_num = T;
    for (int i = 1; i <= m; ++ i)
        scanf("%d", &cap[i]);
    for (int i = 1; i <= n; ++ i){
        int num; scanf("%d", &num);
        for (int j = 1; j <= num; ++j) {
            int temp; scanf("%d", &temp);
            if (!vis[temp]) vis[temp] = i, Add_Edge(S, i, cap[temp]);
            else Add_Edge(vis[temp], i, inf);
            vis[temp] = i;
        }
        int val; scanf("%d", &val);
        Add_Edge(i, T, val);
    }
    printf("%d\n", Dinic());
    return 0;
}
View Code

点限制最大流(拆点最大流

P1231 教辅的组成

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, s, t, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;
}

inline bool Bfs(){
    for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[t] != -1;
}

inline int dfs(int u, int flow){
    if(u == t) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 1; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(s, inf);
    }
    return ans;
}

int main(){
    int cnt = 0;
    memset(head, -1, sizeof(head));
    int n1, n2, n3;
    scanf("%d %d %d", &n1, &n2, &n3); //书,练习册,答案

    s = 1, t = n1*2+n2+n3+2;
    node_num = t;
    for (int i = 1; i <= n1; ++ i) {
        int book1 = (i-1)*2+1+1, book2 = book1 + 1;
        Add_Edge(book1, book2, 1), Add_Edge(book2, book1, 0);
        //cout << book1 << "---" << book2 << endl;
    }
    for (int i = 1; i <= n2; ++ i) {
        int v = i + 2*n1+1;
        Add_Edge(s, v, 1), Add_Edge(v, s, 0);
        //cout << s << "---" << v << endl;
    }
    for (int i = 1; i <= n3; ++ i) {
        int v = n1*2+n2+i+1;
        Add_Edge(v, t, 1), Add_Edge(t, v, 0);
        //cout << v << "---" << t << endl;
    }


    int m1, m2;
    scanf("%d", &m1);
    for (int i = 1, u, v; i <= m1; i++) {
        scanf("%d %d", &u, &v);
        int book1 = (u-1)*2+1+1;
        v = n1*2+1+v;
        Add_Edge(v, book1, 1), Add_Edge(book1, v, 0);
        //cout << v << "---" << book1 << endl;
    }

    scanf("%d", &m2);
    for (int i = 1, u, v; i <= m2; i++) {
        scanf("%d %d", &u, &v);
        int book2 = (u-1)*2+1+1+1, an = v+n1*2+n2+1;
        Add_Edge(book2, an, 1), Add_Edge(an, book2, 0);
        //cout << book2 << "---" << an << endl;
    }
    printf("%d\n", Dinic());
    return 0;
}
View Code

P1402 酒店之王

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, s, t, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;
}

inline bool Bfs(){
    for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[t] != -1;
}

inline int dfs(int u, int flow){
    if(u == t) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 1; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(s, inf);
    }
    return ans;
}

int main(){
    cnt = 0;
    memset(head, -1, sizeof(head));
    int n1, n2, n3;
    scanf("%d %d %d", &n1, &n2, &n3); //书,练习册,答案

    s = 1, t = n1*2+n2+n3+2;
    node_num = t;

    for (int i = 1; i <= n1; ++ i) {
        int people1 = (i-1)*2+1+1, people2 = people1 + 1;
        Add_Edge(people1, people2, 1), Add_Edge(people2, people1, 0);
    }
    for (int i = 1; i <= n2; ++ i) {
        int v = i + 2*n1+1;
        Add_Edge(s, v, 1), Add_Edge(v, s, 0);
    }
    for (int i = 1; i <= n3; ++ i) {
        int v = n1*2+n2+i+1;
        Add_Edge(v, t, 1), Add_Edge(t, v, 0);
    }


    for (int i = 1; i <= n1; i++) {
        for (int j = 1, temp; j <= n2; ++ j) {
            scanf("%d", &temp);
            if (!temp) continue;
            int people1 = (i-1)*2+1+1, v = n1*2+1+j;
            Add_Edge(v, people1, 1), Add_Edge(people1, v, 0);
        }

    }

    for (int i = 1; i <= n1; i++) {
        for (int j = 1, temp; j <= n3; ++ j) {
            scanf("%d", &temp);
            if (!temp) continue;
            int people2 = (i-1)*2+1+1+1, an = j+n1*2+n2+1;
            Add_Edge(people2, an, 1), Add_Edge(an, people2, 0);
        }
    }
    printf("%d\n", Dinic());
    return 0;
}
View Code

P2472 [SCOI2007]蜥蜴

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e7 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, s, t, k, r, d;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}

inline bool Bfs(){
    for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[t] != -1;
}

inline int dfs(int u, int flow){
    if(u == t) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 1; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(s, inf);
    }
    return ans;
}

inline int id1(int x, int y) { return (x-1)*m+y; }
inline int id2(int x, int y) { return (x-1)*m+y+n*m; }
inline int cal(int x1, int y1, int x2, int y2) { return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2); }
char g[205][205];
char gg[205][205];
int id[205][205];
char str[30];
int main() {
    cnt = 0;
    memset(head, -1, sizeof(head));
    scanf("%d%d%d", &n, &m, &d);
    s = 1, t = n*m*2+2;//每个点拆成两个点,表示从石柱顶连向石柱下端
    node_num = t;
    for(int i = 1; i <= n; i ++) {
        scanf("%s", str+1);
        for(int j = 1; j <= m; j ++) if(str[j]-'0' > 0) Add_Edge(id1(i, j)+1, id2(i, j)+1, str[j]-'0');
    }//这个点有石柱,石柱顶连向石柱下端,容量为石柱高
    int res = 0;
    for(int i = 1; i <= n; i ++) {
        scanf("%s", str+1);
        for(int j = 1; j <= m; j ++) if(str[j] == 'L') Add_Edge(s, id1(i, j)+1, 1), res ++;
    }//这个点有蜥蜴,源点连向石柱的上端
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= m; j ++)
            if(i-d < 1 || i+d > n || j-d < 1 || j+d > m) Add_Edge(id2(i, j)+1, t, inf);//可以跳出去,石柱的底连向汇点
    for(int x1 = 1; x1 <= n; x1 ++)//距离小于等于d,可以跳到的两个点连边
        for(int y1 = 1; y1 <= m; y1 ++)
            for(int x2 = 1; x2 <= n; x2 ++)
                for(int y2 = 1; y2 <= m; y2 ++)
                    if(cal(x1, y1, x2, y2) <= d*d) Add_Edge(id2(x1, y1)+1, id1(x2, y2)+1, inf);

    printf("%d\n", res-Dinic());
    return 0;
}
View Code

P2763 试题库问题

这一题的输出方式值得记住;

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, S, T, k, d;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline int read() {
    char c = getchar(); int x = 0, f = 1;
    for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}

inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline int dfs(int u, int flow){
    if(u == T) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

int main() {
    init();
    n = read(), m = read();
    S = 0, T = n+m+1;
    int sum = 0;
    node_num = T;
    for (int i = 1, w; i <= n; ++ i) {
        scanf("%d", &w);
        Add_Edge(S, i, w);
        sum += w;
    }
    for (int i = 1; i <= m; ++ i) {
        int num;
        scanf("%d", &num);
        Add_Edge(i+n, T, 1);
        for (int j = 1; j <= num; ++ j) {
            int temp;
            scanf("%d", &temp);
            Add_Edge(temp, i+n, 1);
        }
    }
    if (Dinic() != sum)
        cout << "No Solution!" << endl;
    else {
        for (int i = 1; i <= n; ++ i){
            cout << i << ":";
            for (int j = head[i]; ~j; j = edge[j].next)
                if (edge[j].to > 0 && !edge[j].w)
                  printf(" %d", edge[j].to-n);
            cout << endl;
        }
    }
    return 0;
}
View Code

P2891 [USACO07OPEN]Dining G

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, S, T, f, d;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline int read() {
    char c = getchar(); int x = 0, f = 1;
    for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}

inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline int dfs(int u, int flow){
    if(u == T) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

int main() {
    init();
    scanf("%d %d %d", &n, &f, &d);
    S = 0, T = f+d+2*n+1;
    node_num = T;
    int sum = 0;
    for (int i = 1; i <= f; ++i)
        Add_Edge(S, i+n*2, 1);
    for (int i = 1; i <= d; ++i)
        Add_Edge(i+f+n*2, T, 1);
    for (int i = 1; i <= n; ++ i) {
        int fi, di;
        Add_Edge((i-1)*2+1, (i-1)*2+2, 1);
        scanf("%d %d", &fi, &di);
        for (int j = 1, tf; j <= fi; ++ j) {
            scanf("%d", &tf);
            Add_Edge(tf+n*2, (i-1)*2+1, 1);
        }
        for (int j = 1, td; j <= di; ++ j) {
            scanf("%d", &td);
            Add_Edge((i-1)*2+2, td+f+n*2, 1);
        }
    }
    printf("%d\n", Dinic());
    return 0;
}
View Code

P3254 圆桌问题

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define pii pair<long long, int>
typedef long long ll;

const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const ll inf = 1e18;
const int mod = 1e9 + 7;


struct Edge{
    int to;
    int next;
    ll w;
} edge[maxn*2];

int head[maxn], node_num;
int cur[maxn];//当前弧优化数组
int n, m, S, T, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
bool vis[maxn];

inline void init(){
    cnt = 0;
    for (int i = 0; i <= node_num; ++ i) head[i] = -1;
}

inline void Add_Edge(int u, int v, ll w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}


inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline ll dfs(int u, ll flow){
    if(u == T) return flow;
    ll del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            ll ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline ll Dinic() {
    ll ans = 0;
    while(Bfs()) {
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

int main() {
    scanf("%d %d", &m, &n);
    S = 0, T = n+m+1;
    node_num = T;
    init();
    int rec_n[n+1], rec_m[m+1];
    int sum = 0;
    for (int i = 1; i <= m; ++ i) {
        scanf("%d", &rec_m[i]);
        sum += rec_m[i];
        Add_Edge(S, i, rec_m[i]);
        for (int j = 1; j <= n; ++ j)
            Add_Edge(i, j+m, 1);
    }
    for (int i = 1; i <= n; ++ i) {
        scanf("%d", &rec_n[i]);
        Add_Edge(i+m, T, rec_n[i]);
    }
    int flag = 0;
    if (Dinic() == sum) flag = 1;
    cout << flag << endl;
    if (flag) {
        vector<int> rec[m+1];
        for (int u = 1; u <= m; ++ u) {
            for (int j = head[u]; ~j; j = edge[j].next) {
                if (edge[j].w == 0) {
                    rec[u].push_back(edge[j].to-m);
                }
            }
        }
        for (int i = 1; i <= m; ++ i) {
            cout << rec[i][0];
            for (int j = 1; j < rec[i].size(); ++ j)
                cout << " " << rec[i][j];
            cout << endl;
        }
    }
    return 0;
}
View Code

最小割:

裸最小割:

P3931 SAC E#1 - 一道难题 Tree

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, s, t, x;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;
}

inline bool Bfs(){
    for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[t] != -1;
}

inline int dfs(int u, int flow){
    if(u == t) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 1; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(s, inf);
    }
    return ans;
}

map<pair<int, int>, int> wei;
vector<int> G[maxn];
int vis[maxn];
void DFS(int st)
{
    vis[st] = 1;
    for (int i=0; i < G[st].size(); ++ i) {
        int v = G[st][i];
        if (vis[v]) continue;
        Add_Edge(st, v, wei[{st, v}]), Add_Edge(v, st, 0);
        //cout << st << " ---- " << v << "  " << wei[{st, v}] << endl;
        if (G[v].size() == 1) {
            //cout << v << " ---- " << t << endl;
            Add_Edge(v, t, inf), Add_Edge(t, v, 0);
        }
        else
            DFS(v);
    }
}

int main(){
    init();
    int root;
    scanf("%d %d", &n, &root);
    s = 1, t = n+2;
    node_num = n+2;
    Add_Edge(s, root+1, inf), Add_Edge(root+1, s, 0);
    for (int i = 1; i <= n-1; i++) {
        int u, v, w;
        scanf("%d %d %d", &u, &v, &w);
        u ++, v ++;
        wei[{u, v}] = w, wei[{v, u}] = w;
        G[u].push_back(v), G[v].push_back(u);
    }
    DFS(root+1);
    printf("%d\n", Dinic());
    return 0;
}
View Code

P2057 [SHOI2007]善意的投票 / [JLOI2010]冠军调查

这道题建双向边这里挺耐人寻味的。

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, s, t, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;
}

inline bool Bfs(){
    for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[t] != -1;
}

inline int dfs(int u, int flow){
    if(u == t) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 1; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(s, inf);
    }
    return ans;
}

int main(){
    cnt = 0;
    memset(head, -1, sizeof(head));

    scanf("%d %d", &n, &m); //书,练习册,答案
    s = 1, t = n+2;
    node_num = t;

    for (int i = 1,temp; i <= n; ++ i) {
        scanf("%d", &temp);
        if (temp) Add_Edge(s, i+1, 1), Add_Edge(i+1, s, 0);
        else Add_Edge(i+1, t, 1), Add_Edge(t, i+1, 0);
    }


    for (int i = 1, u, v; i <= m; i++) {
        scanf("%d %d", &u, &v);
        Add_Edge(u+1, v+1, 1), Add_Edge(v+1, u+1, 1), Add_Edge(u+1, v+1, 0), Add_Edge(v+1, u+1, 0);
    }
    printf("%d\n", Dinic());
    return 0;
}
View Code

P1344 [USACO4.4]追查坏牛奶Pollutant Control

这题有一个如何统计割边数的小技巧:

就是给边权加上一个大数+1之后再模这个大数,剩下的就是割边数了

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn = 1e6 + 10;
const ll maxm = 1e5 * 2 + 10;
const ll inf = 0x3f3f3f3f;
const ll mod = 1e9 + 7;

struct Edge{
    ll to;
    ll next;
    ll w;
}edge[maxn*2];

ll head[maxn];
ll cur[maxn];//当前弧优化数组
ll n, m, s, t, k;//点数,边数,源点,汇点
ll dis[maxn];//Bfs深度
ll cnt = 0;//边数
ll node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline void Add_Edge(ll u, ll v, ll w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;
}

inline bool Bfs(){
    for(ll i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[s] = 0;
    queue<ll> q;
    q.push(s);
    while(!q.empty()){
        ll u = q.front(); q.pop();
        for(ll i = head[u]; i != -1; i = edge[i].next){
            ll v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[t] != -1;
}

inline ll dfs(ll u, ll flow){
    if(u == t) return flow;
    ll del = flow;
    for(ll i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        ll v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            ll ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline ll Dinic(){
    ll ans = 0;
    while(Bfs()){
        for(ll i = 1; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(s, inf);
    }
    return ans;
}

int main(){
    ll cnt = 0;
    memset(head, -1, sizeof(head));

    scanf("%lld %lld", &n, &m);

    s = 1, t = n;
    node_num = t;
    for (ll i = 1, u, v, w; i <= m; ++ i) {
        scanf("%lld %lld %lld", &u, &v, &w);
        w = w * 1050 + 1;
        Add_Edge(u, v, w), Add_Edge(v, u, 0);
    }
    ll temp = Dinic();
    printf("%lld %lld\n", temp/1050, temp % 1050);
    return 0;
}
View Code

P2944 [USACO09MAR]Earthquake Damage 2 G

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, S, T, p;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline int read() {
    char c = getchar(); int x = 0, f = 1;
    for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}

bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

int dfs(int u, int flow){
    if(u == T) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

int main() {
    init();
    scanf("%d %d %d", &n, &m, &p);
    S = 1, T = 2*n+1;
    node_num = T;
    Add_Edge(S, S+1, inf);
    for (int i = 1, u, v; i <= m; ++ i) {
        scanf("%d %d", &u, &v);
        Add_Edge(u*2, v*2-1, inf);
        Add_Edge(v*2, u*2-1, inf);
    }
    int rec[n+1] = {0};
    for (int i = 1, pi; i <= p; ++ i) {
        scanf("%d", &pi);
        Add_Edge(pi*2-1, pi*2, inf);
        Add_Edge(pi*2, T, inf);
        rec[pi] = 1;
    }
    for (int i = 2; i <= n; ++ i) {
        if (!rec[i]) Add_Edge(i*2-1, i*2, 1);
    }
    printf("%d\n", Dinic());
    return 0;
}
View Code

P3163 [CQOI2014]危桥

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, S, T, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;
int rec[100+5][100+5];

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline int read() {
    char c = getchar(); int x = 0, f = 1;
    for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}

inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline int dfs(int u, int flow){
    if(u == T) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

bool judge(int mid) {
    init();
    for (int i = 1; i <= n; ++ i) {
        Add_Edge(S, i, mid);
        Add_Edge(i, i+n, k);
        for (int j = 1; j <= n; ++j) {
            if (rec[i][j]) Add_Edge(i, j+2*n, 1);
            else Add_Edge(i+n, j+3*n, 1);
        }
    }
    for (int i = 1; i <= n; ++ i) {
        Add_Edge(i+3*n,i+2*n, k);
        Add_Edge(i+2*n, T, mid);
    }
    return Dinic() == mid * n;
}
char g[105][105];
int main() {

    int a1, a2, an, b1, b2, bn;
    while (~scanf("%d %d %d %d %d %d %d %d", &n, &a1, &a2, &an, &b1, &b2, &bn)) {
        init();
        ++ a1, ++ a2, ++ b1, ++ b2;
        S = 0, T = n+1;
        node_num = T;
        Add_Edge(S, a1, inf), Add_Edge(S, b1, inf);
        Add_Edge(a2, T, inf), Add_Edge(b2, T, inf);
        for (int i = 1; i <= n; ++ i) {
            for (int j = 1; j <= n; ++ j) {
                cin >> g[i][j];
                if (g[i][j] == 'X') continue;
                else if(g[i][j] == 'O') Add_Edge(i, j, 2);
                else Add_Edge(i, j, inf);
            }
        }
        int flag = 0;
        if (Dinic() >= 2*(an+bn)) flag = 1;
        if (flag) {
            init();
            Add_Edge(S, a1, inf), Add_Edge(S, b2, inf);
            Add_Edge(a2, T, inf), Add_Edge(b1, T, inf);
            for (int i = 1; i <= n; ++ i) {
                for (int j = 1; j <= n; ++ j) {
                    if (g[i][j] == 'X') continue;
                    else if(g[i][j] == 'O') Add_Edge(i, j, 2);
                    else Add_Edge(i, j, inf);
                }
            }
            if (Dinic() >= 2*(an+bn))
                cout << "Yes" << endl;
            else cout << "No" << endl;
        }
        else cout << "No" << endl;
    }
    return 0;
}
View Code

P3866 [TJOI2009]战争游戏

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define pii pair<long long, int>
typedef long long ll;

const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const ll inf = 1e18;
const int mod = 1e9 + 7;


struct Edge{
    int to;
    int next;
    ll w;
} edge[maxn*2];

int head[maxn], node_num;
int cur[maxn];//当前弧优化数组
int n, m, S, T, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
bool vis[maxn];

inline void init(){
    cnt = 0;
    for (int i = 0; i <= node_num; ++ i) head[i] = -1;
}

inline void Add_Edge(int u, int v, ll w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}


inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline ll dfs(int u, ll flow){
    if(u == T) return flow;
    ll del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            ll ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline ll Dinic() {
    ll ans = 0;
    while(Bfs()) {
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

int dic[4][2] = {1,0, -1,0, 0,-1, 0,1};

int main() {
    scanf("%d %d", &n, &m);
    S = 0, T = 2*n*m+1;
    node_num = T;
    init();
    int sum = 0;
    int g[n+1][m+1];
    for (int i = 1; i <= n; ++ i) {
       for (int j = 1; j <= m; ++ j) {
            scanf("%d", &g[i][j]);
            int id = (i-1)*m+j, id1 = id+n*m;
            if (g[i][j] > 0)
                Add_Edge(id, id1, g[i][j]);
            else if (g[i][j] == 0)
                Add_Edge(S, id, inf), Add_Edge(id, id1, inf);
       }
    }

    for (int i = 1; i <= n; ++ i) {
       for (int j = 1; j <= m; ++ j) {
            if (g[i][j] == -1) continue;
            for (int k = 0; k < 4; ++ k) {
                int tx = i + dic[k][0], ty = j + dic[k][1];
                int id = (i-1)*m+j, id1 = id+n*m;
                int td = (tx-1)*m+ty, td1 = (tx-1)*m+ty+n*m;
                if (tx < 1 || ty < 1 || tx > n || ty > m) {
                    Add_Edge(id1, T, inf);
                    continue;
                }
                if (g[tx][ty] == -1) continue;
                Add_Edge(id1, td, inf);
            }
       }
    }
    cout << Dinic() << endl;
    return 0;
}
View Code

P4001 [ICPC-Beijing 2006]狼抓兔子

这题是我用ISAP卡过去的,正解应该是对偶图最短路等于最小割

UPD20.09.23: 已在对偶图专题更新。

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e7 + 10;
//const int maxm = 1e3 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int m,n,S,T,k, x, head[maxn], num_edge;
int cur[maxn],deep[maxn],last[maxn],num[maxn];
int node_num;
int cap[40];
int wanted[maxn][40];
//cur当前弧优化; last该点的上一条边; num桶 用来GAP优化
struct Edge{
    int next,to,dis;
}edge[maxn*2];

void add_edge(int from,int to,int dis)
{
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    edge[num_edge].next=head[from];
    head[from]=num_edge++;
//
//    edge[num_edge].to=from;
//    edge[num_edge].dis=0;
//    edge[num_edge].next=head[to];
//    head[to]=num_edge++;
}
//bfs仅用于更新deep
void bfs(int T)
{
    queue<int>q;
    for (int i=0; i<=node_num; i++) cur[i]=head[i];
    for (int i=0; i<=node_num; i++) deep[i]=node_num;
    deep[T]=0;
    q.push(T);
    while (!q.empty())
    {
        int now=q.front(); q.pop();
        for (int i=head[now]; i != -1; i=edge[i].next)
        {
            if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边
            {
                deep[edge[i].to] = deep[now]+1;
                q.push(edge[i].to);
            }
        }
    }
}

int add_flow(int S,int T)
{
    int ans=inf,now=T;
    while (now!=S)
    {
        ans=min(ans,edge[last[now]].dis);
        now=edge[last[now]^1].to;
    }
    now=T;
    while (now!=S)
    {
        edge[last[now]].dis-=ans;
        edge[last[now]^1].dis+=ans;
        now=edge[last[now]^1].to;
    }
    return ans;
}

int isap()
{
    int now=S;
    int maxflow = 0;
    bfs(T);//搜出一条增广路
    for (int i=1; i<=node_num; i++) num[deep[i]]++;
    while (deep[S]<node_num)
    {
        if (now==T)
        {//如果到达汇点就直接增广,重新回到源点进行下一轮增广
            maxflow+=add_flow(S,T);
            now=S;//回到源点
        }
        bool has_find=0;
        for (int i=cur[now]; i!=-1; i=edge[i].next)
        {
            if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路
            {
                has_find=true;
                cur[now]=i;//当前弧优化
                now=edge[i].to;
                last[edge[i].to]=i;
                break;
            }
        }
        if (!has_find)//没有找到出边,重新编号
        {
            int minn=node_num-1;
            for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径
                if (edge[i].dis)
                    minn=min(minn,deep[edge[i].to]);
            if ((--num[deep[now]])==0) break;//GAP优化 出现了断层
            num[deep[now]=minn+1]++;
            cur[now]=head[now];
            if (now!=S)
                now=edge[last[now]^1].to;
        }
    }
    return maxflow;
}

void init()
{
    num_edge = 0;
    memset(head,-1,sizeof(head));
}

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int main() {
    n = read(), m = read();
    init();
    S = 1, T = n*m;
    node_num = T;
    for (int i = 1; i <= n; ++ i) {
        int w;
        for (int j = 2; j <= m; ++ j) {
            w = read();
            int id1 = (i-1)*m+j-1, id2 = (i-1)*m+j;
            add_edge(id1, id2, w);
            add_edge(id2, id1, w);
        }
    }

    for (int i = 2; i <= n; ++ i) {
        int w;
        for (int j = 1; j <= m; ++ j) {
            w = read();
            int id1 = (i-2)*m+j, id2 = (i-1)*m+j;
            add_edge(id1, id2, w);
            add_edge(id2, id1, w);
        }
    }

    for (int i = 2; i <= n; ++ i) {
        int w;
        for (int j = 2; j <= m; ++ j) {
            w = read();
            int id1 = (i-2)*m+j-1, id2 = (i-1)*m+j;
            add_edge(id1, id2, w);
            add_edge(id2, id1, w);
        }
    }
    cout << isap() << endl;
    return 0;
}
View Code

割点转割边:

P1345 [USACO5.4]奶牛的电信T

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
//const int maxm = 1e3 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int m,n,s,t, x, head[maxn], num_edge;
int cur[maxn],deep[maxn],last[maxn],num[maxn];
int node_num;
//cur当前弧优化; last该点的上一条边; num桶 用来GAP优化
struct Edge{
    int next,to,dis;
}edge[maxn*2];

void add_edge(int from,int to,int dis)
{
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    edge[num_edge].next=head[from];
    head[from]=num_edge++;
}
//bfs仅用于更新deep
void bfs(int t)
{
    queue<int>q;
    for (int i=0; i<=node_num; i++) cur[i]=head[i];
    for (int i=1; i<=node_num; i++) deep[i]=node_num;
    deep[t]=0;
    q.push(t);
    while (!q.empty())
    {
        int now=q.front(); q.pop();
        for (int i=head[now]; i != -1; i=edge[i].next)
        {
            if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边
            {
                deep[edge[i].to] = deep[now]+1;
                q.push(edge[i].to);
            }
        }
    }
}

int add_flow(int s,int t)
{
    int ans=inf,now=t;
    while (now!=s)
    {
        ans=min(ans,edge[last[now]].dis);
        now=edge[last[now]^1].to;
    }
    now=t;
    while (now!=s)
    {
        edge[last[now]].dis-=ans;
        edge[last[now]^1].dis+=ans;
        now=edge[last[now]^1].to;
    }
    return ans;
}
int isap(int s,int t)
{
    int now=s;
    int maxflow = 0;
    bfs(t);//搜出一条增广路
    for (int i=1; i<=node_num; i++) num[deep[i]]++;
    while (deep[s]<node_num)
    {
        if (now==t)
        {//如果到达汇点就直接增广,重新回到源点进行下一轮增广
            maxflow+=add_flow(s,t);
            now=s;//回到源点
        }
        bool has_find=0;
        for (int i=cur[now]; i!=-1; i=edge[i].next)
        {
            if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路
            {
                has_find=true;
                cur[now]=i;//当前弧优化
                now=edge[i].to;
                last[edge[i].to]=i;
                break;
            }
        }
        if (!has_find)//没有找到出边,重新编号
        {
            int minn=node_num-1;
            for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径
                if (edge[i].dis)
                    minn=min(minn,deep[edge[i].to]);
            if ((--num[deep[now]])==0) break;//GAP优化 出现了断层
            num[deep[now]=minn+1]++;
            cur[now]=head[now];
            if (now!=s)
                now=edge[last[now]^1].to;
        }
    }
    return maxflow;
}
int main(){
    num_edge = 0;
    memset(head,-1,sizeof(head));
    scanf("%d%d%d%d",&n,&m,&s,&t);
    node_num = 2*n;
    for (int i = 1; i <= n; ++ i)
        add_edge(i,i+n,1), add_edge(i+n,i,0);

    for(int i=0;i<m;i++){
        int u,v,cap;
        scanf("%d%d",&u,&v);
        add_edge(u+n,v,inf), add_edge(v,u+n,0);
        add_edge(v+n,u,inf), add_edge(u,v+n,0);
    }

    int ans = isap(s+n,t);
    printf("%d\n", ans);

    return 0;
}
View Code

 

最大权闭合子图:

P1361 小M的作物

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
//const int maxm = 1e3 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int m,n,s,t, x, head[maxn], num_edge;
int cur[maxn],deep[maxn],last[maxn],num[maxn];
int node_num;
//cur当前弧优化; last该点的上一条边; num桶 用来GAP优化
struct Edge{
    int next,to,dis;
}edge[maxn*2];

void add_edge(int from,int to,int dis)
{
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    edge[num_edge].next=head[from];
    head[from]=num_edge++;
}
//bfs仅用于更新deep
void bfs(int t)
{
    queue<int>q;
    for (int i=0; i<=node_num; i++) cur[i]=head[i];
    for (int i=1; i<=node_num; i++) deep[i]=node_num;
    deep[t]=0;
    q.push(t);
    while (!q.empty())
    {
        int now=q.front(); q.pop();
        for (int i=head[now]; i != -1; i=edge[i].next)
        {
            if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边
            {
                deep[edge[i].to] = deep[now]+1;
                q.push(edge[i].to);
            }
        }
    }
}

int add_flow(int s,int t)
{
    int ans=inf,now=t;
    while (now!=s)
    {
        ans=min(ans,edge[last[now]].dis);
        now=edge[last[now]^1].to;
    }
    now=t;
    while (now!=s)
    {
        edge[last[now]].dis-=ans;
        edge[last[now]^1].dis+=ans;
        now=edge[last[now]^1].to;
    }
    return ans;
}
int isap(int s,int t)
{
    int now=s;
    int maxflow = 0;
    bfs(t);//搜出一条增广路
    for (int i=1; i<=node_num; i++) num[deep[i]]++;
    while (deep[s]<node_num)
    {
        if (now==t)
        {//如果到达汇点就直接增广,重新回到源点进行下一轮增广
            maxflow+=add_flow(s,t);
            now=s;//回到源点
        }
        bool has_find=0;
        for (int i=cur[now]; i!=-1; i=edge[i].next)
        {
            if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路
            {
                has_find=true;
                cur[now]=i;//当前弧优化
                now=edge[i].to;
                last[edge[i].to]=i;
                break;
            }
        }
        if (!has_find)//没有找到出边,重新编号
        {
            int minn=node_num-1;
            for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径
                if (edge[i].dis)
                    minn=min(minn,deep[edge[i].to]);
            if ((--num[deep[now]])==0) break;//GAP优化 出现了断层
            num[deep[now]=minn+1]++;
            cur[now]=head[now];
            if (now!=s)
                now=edge[last[now]^1].to;
        }
    }
    return maxflow;
}
int main(){
    num_edge = 0;
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    int cap1[n+2], cap2[n+2];
    int sum = 0;
    for (int i = 2; i < n+2; i++) scanf("%d", &cap1[i]), sum += cap1[i];
    for (int i = 2; i < n+2; i++) scanf("%d", &cap2[i]), sum += cap2[i];
    scanf("%d", &m);
    for (int i = 2; i < n+2; ++ i) {
        add_edge(1, i, cap1[i]), add_edge(i, 1, 0);
        add_edge(i, n+2*m+2, cap2[i]), add_edge(n+2*m+2, i, 0);
    }
    node_num = n+2*m+2;
    s = 1, t = n+2*m+2;
    for (int i = 1, ki, c1, c2; i <= m; ++ i) {
        scanf("%d%d%d", &ki, &c1, &c2);
        sum += c1 + c2;
        int add1 = (n+1)+(i-1)*2+1, add2 = (n+1)+(i-1)*2+2;
        add_edge(s, add1, c1), add_edge(add1, s, 0);
        add_edge(add2, t, c2), add_edge(t, add2, 0);
        for (int j = 1, tp; j <= ki; ++ j) {
            scanf("%d", &tp); tp += 1;
            add_edge(add1, tp, inf), add_edge(tp, add1, 0);
            add_edge(tp, add2, inf), add_edge(add2, tp, 0);
        }
    }

    int ans = sum - isap(s,t);
    printf("%d\n", ans);

    return 0;
}
View Code

P2762 太空飞行计划问题

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, S, T, k, d;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline int read() {
    char c = getchar(); int x = 0, f = 1;
    for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}

inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline int dfs(int u, int flow){
    if(u == T) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

int main() {
    init();
    m = read(), n = read();
    S = 0, T = n+m+1;
    node_num = T;
    int sum = 0;
    for (int i = 1, w; i <= m; ++ i) {
        scanf("%d", &w), sum += w;
        Add_Edge(S, i, w);
        while(getchar() == ' ') {
            scanf("%d", &w);
            Add_Edge(i, m+w,inf);
        }
    }
    for (int i = 1, w; i <= n; ++ i) {
        w = read();
        Add_Edge(i+m, T, w);
    }
    sum -= Dinic();
    for (int i = 1; i <= m; i++)
        if (~dis[i])
            printf("%d ",i);
    printf("\n");
    for (int i = 1; i <= n; i++)
        if (~dis[m+i])
            printf("%d ", i);
    printf("\n%d\n", sum);
    return 0;
}
View Cod

P3410 拍照

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define pii pair<long long, int>
typedef long long ll;

const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const ll inf = 1e18;
const int mod = 1e9 + 7;


struct Edge{
    int to;
    int next;
    ll w;
} edge[maxn*2];

int head[maxn], node_num;
int cur[maxn];//当前弧优化数组
int n, m, S, T, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
bool vis[maxn];

inline void init(){
    cnt = 0;
    for (int i = 0; i <= node_num; ++ i) head[i] = -1;
}

inline void Add_Edge(int u, int v, ll w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}


inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline ll dfs(int u, ll flow){
    if(u == T) return flow;
    ll del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            ll ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline ll Dinic() {
    ll ans = 0;
    while(Bfs()) {
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

int main() {
    scanf("%d %d", &m, &n);
    S = 0, T = n+m+1;
    node_num = T;
    init();
    int sum = 0;
    for (int i = 1, temp; i <= m; ++ i) {
        int cost;
        scanf("%d", &cost), sum += cost;
        Add_Edge(i+n, T, cost);
        while (scanf("%d", &temp) && temp)
            Add_Edge(temp, i+n, inf);
    }
    for (int i = 1, cost; i <= n; ++ i)
        scanf("%d", &cost), Add_Edge(S, i, cost);
    printf("%d\n", sum-Dinic());
    return 0;
}
View Code

P3872 [TJOI2010]电影迷

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define pii pair<long long, int>
typedef long long ll;

const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const ll inf = 1e18;
const int mod = 1e9 + 7;


struct Edge{
    int to;
    int next;
    ll w;
} edge[maxn*2];

int head[maxn], node_num;
int cur[maxn];//当前弧优化数组
int n, m, S, T, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
bool vis[maxn];

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof(head));
}

inline void Add_Edge(int u, int v, ll w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}


inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline ll dfs(int u, ll flow){
    if(u == T) return flow;
    ll del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            ll ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline ll Dinic() {
    ll ans = 0;
    while(Bfs()) {
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

int main() {
    scanf("%d %d", &n, &m);
    S = 0, T = n+1;
    node_num = T;
    init();
    int sum = 0;
    for (int i = 1, w; i <= n; ++ i) {
        scanf("%d", &w);
        if (w > 0) Add_Edge(S, i, w), sum += w;
        else Add_Edge(i, T, -w);
    }

    for (int i = 1,u,v,w; i <= m; ++ i) {
        scanf("%d %d %d", &u, &v, &w);
        Add_Edge(u, v, w);
    }
    cout << sum - Dinic() << endl;
    return 0;
}
View Code

P4174 [NOI2006]最大获利

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
typedef long long ll;

const int maxn = 2e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
} edge[maxn*2];

int head[maxn], node_num;
int cur[maxn];//当前弧优化数组
int n, m, S, T, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof(head));
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}


inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline int dfs(int u, int flow){
    if(u == T) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic() {
    int ans = 0;
    while(Bfs()) {
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int main() {
    n = read(), m = read();
    S = 0, T = n+m+1;
    node_num = T;
    init();
    int sum = 0;
    for (int i = 1, ci; i <= n; ++ i) {
        ci = read();
        Add_Edge(m+i, T, ci);
    }
    for (int i = 1,u,v,w; i <= m; ++ i) {
        u = read(), v = read(), w = read();
        Add_Edge(S, i, w);
        sum += w;
        Add_Edge(i, u+m, inf);
        Add_Edge(i, v+m, inf);
    }
    cout << sum - Dinic() << endl;
    return 0;
}
View Code

P4313 文理分科

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
typedef long long ll;

const int maxn = 2e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
} edge[maxn*2];

int head[maxn], node_num;
int cur[maxn];//当前弧优化数组
int n, m, S, T, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof(head));
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}


inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline int dfs(int u, int flow){
    if(u == T) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic() {
    int ans = 0;
    while(Bfs()) {
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int dic[5][2] = {1,0,-1,0,0,-1,0,1,0,0};

int main()
{
    n=read(),m=read();
    S = 0, T = 3*n*m+1;
    node_num =3*n*m+1;
    int sum = 0;
    init();
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=m;j++)
        {
            int tmp=read();
            Add_Edge(S,(i-1)*m+j,tmp);
            sum+=tmp;
        }
    }
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=m;j++)
        {
            int tmp=read();
            Add_Edge((i-1)*m+j,T,tmp);
            sum+=tmp;
        }
    }
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=m;j++)
        {
            int tmp=read();
            Add_Edge(S,n*m+(i-1)*m+j,tmp);
            sum+=tmp;
        }
    }
    for (int i=1;i<=n;i++)
    {
        for (int j=1;j<=m;j++)
        {
            int tmp=read();
            Add_Edge(2*n*m+(i-1)*m+j,T,tmp);
            sum+=tmp;
        }
    }
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++)
        {
            for (int k=0;k<5;k++)
            {
                int nx=i+dic[k][0],ny=j+dic[k][1];
                if (nx<1 || nx>n || ny<1 || ny>m)
                    continue;
                Add_Edge(n*m+(nx-1)*m+ny,(i-1)*m+j,inf);
                Add_Edge((i-1)*m+j,2*n*m+(nx-1)*m+ny,inf);
            }
        }
    cout << sum-Dinic() << endl;
    return 0;
}
View Code

较为经典的最小割:

P2774 方格取数问题

View Code

P2598 [ZJOI2009]狼和羊的故事

View Code

P4474 王者之剑

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, S, T;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline int read() {
    char c = getchar(); int x = 0, f = 1;
    for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}

inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline int dfs(int u, int flow){
    if(u == T) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

int g[205][205];
int dic[4][2] = {1,0, -1,0, 0,-1, 0,1};

int main() {
    init();
    scanf("%d %d", &n, &m);
    S = 0, T = n*m+1;
    node_num = T;
    int sum = 0;
    for (int i = 1; i <= n; ++ i)
        for (int j = 1; j <= m; ++ j)
            scanf("%d", &g[i][j]), sum += g[i][j];

    for (int i = 1; i <= n; ++ i) {
        for (int j = 1; j <= m; ++ j) {
            int id = (i-1)*m+j;
            if ((i+j) % 2 == 0) {
                Add_Edge(S, id, g[i][j]);
                for (int k = 0; k < 4; ++ k) {
                    int tx = i + dic[k][0], ty = j + dic[k][1];
                    int id1 = (tx-1)*m+ty;
                    if (tx < 1 || ty < 1 || tx > n || ty > m) continue;
                    Add_Edge(id, id1, inf);
                }
            }
            else Add_Edge(id, T, g[i][j]);
        }
    }
    printf("%d\n",sum - Dinic());
    return 0;
}
View Code



二分图网络流(Dinic跟二分图跟配哦):

最大匹配:

P2071 座位安排

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, s, t, x;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;
}

inline bool Bfs(){
    for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[t] != -1;
}

inline int dfs(int u, int flow){
    if(u == t) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 1; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(s, inf);
    }
    return ans;
}

int main(){
    init();
    scanf("%d",&n);
    node_num = 2+n*3;
    s = 1, t = 2+n*3;
    for (int i = 1, a, b; i <= n*2; ++ i) {
        scanf("%d %d", &a, &b);
        a += 1+n*2, b += 1+n*2;
        Add_Edge(s, i+1, 1), Add_Edge(i+1, s, 0);
        Add_Edge(i+1, a, 1), Add_Edge(a, i+1, 0);
        Add_Edge(i+1, b, 1), Add_Edge(b, i+1, 0);
    }
    for (int i = 1; i <= n; ++ i)
        Add_Edge(i+2*n+1, t, 2), Add_Edge(t, i+2*n+1, 0);

    int ans = Dinic();
    printf("%d\n", ans);
    return 0;
}
View Code

CF387D George and Interesting Graph

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, s, t, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;
}

inline bool Bfs(){
    for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[t] != -1;
}

inline int dfs(int u, int flow){
    if(u == t) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 1; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(s, inf);
    }
    return ans;
}

int main(){
    scanf("%d %d", &n, &m);
    s = 1, t = n*2+2;
    node_num = n*2+2;
    int ans = 1000000;
    int in[n+1] = {0}, out[n+1] = {0};
    pair<int, int> rec[maxn];
    for (int i = 1, u, v; i <= m; i++) {
        scanf("%d %d", &u, &v);
        ++out[u], ++in[v];
        if (u == v) in[v]--;
        rec[i] = {u, v};
    }

    for (int i = 1 ; i <= n; ++ i) {
        init();
        int temp = in[i]+out[i];
        for (int j = 1; j <= n; ++ j)
            Add_Edge(s,j+1,1), Add_Edge(j+1,s,0), Add_Edge(j+n+1,t,1), Add_Edge(t,j+n+1,0);
        for (int j = 1; j <= m; ++ j) {
            if (rec[j].first == i || rec[j].second == i) continue;
            Add_Edge(rec[j].first+1, rec[j].second+1+n, 1), Add_Edge(rec[j].second+1+n, rec[j].first+1, 0);
        }
        ans = min(ans, n-1-2*Dinic()+m+2*n-1-2*temp);
    }
    printf("%d\n", ans);
    return 0;
}
View Code

P2526 [SHOI2001]小狗散步

这一题输出匹配的方式挺有用的。边权为0的边代表是最大流边

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e7 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, s, t, k, r, d;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}

inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[t] != -1;
}

inline int dfs(int u, int flow){
    if(u == t) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(s, inf);
    }
    return ans;
}

double cal(int x1, int y1, int x2, int y2)
{
    return sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
}

int main() {

    cnt = 0;
    memset(head, -1, sizeof(head));
    scanf("%d%d", &n, &m);
    s = 1, t = n+m+2;
    node_num = t;
    pair<int, int> rec_n[n+1], rec_m[m+1];
    for(int i = 1, u, v; i <= n; i ++)
        scanf("%d %d", &rec_n[i].first, &rec_n[i].second);
    for(int i = 1, u, v; i <= m; i ++)
        scanf("%d %d", &rec_m[i].first, &rec_m[i].second);
    for (int i = 2; i <= n ; ++ i) {
        for (int j = 1; j <= m; ++ j) {
            int u1 = rec_n[i-1].first, v1 = rec_n[i-1].second,
                u2 = rec_n[i].first, v2 = rec_n[i].second,
                u3 = rec_m[j].first, v3 = rec_m[j].second;
            if (2*cal(u1, v1, u2, v2) > (cal(u1, v1, u3, v3)+ cal(u2, v2, u3, v3)))
                Add_Edge(i+1, j+n+1, 1);
        }
    }
    for (int i = 2; i <= n ; ++ i)
        Add_Edge(s, i+1, 1);
    for (int i = 1; i <= m; ++ i)
        Add_Edge(i+n+1, t, 1);
    printf("%d\n%d %d ", n+Dinic(), rec_n[1].first, rec_n[1].second);
    for (int i = 2; i <= n; i++) {
        for (int j = head[i+1]; ~j; j = edge[j].next) {
            int v = edge[j].to;
            if (v == s || v == t || edge[j].w) continue;
            printf("%d %d ", rec_m[v-n-1].first, rec_m[v-n-1].second);
        }
        printf("%d %d ", rec_n[i].first, rec_n[i].second);
    }
    return 0;
}
View Code

最大独立集:

P5030 长脖子鹿放置

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, s, t, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;
}

inline bool Bfs(){
    for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[t] != -1;
}

inline int dfs(int u, int flow){
    if(u == t) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 1; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(s, inf);
    }
    return ans;
}
int g[205][205];
int dic[8][2] = {1,3, -1,3, 1,-3, -1,-3, 3,1, 3,-1, -3,1, -3,-1};

bool check(int tx, int ty)
{
    return (tx < 1 || ty < 1 || ty > m || tx > n || g[tx][ty]);
}

int main(){
    init();
    scanf("%d %d %d", &n, &m, &k);
    s = 1, t = n*m+2;
    node_num = n*m+2;
    int ans = 0;
    for (int i = 1, u, v; i <= k; i++)
        scanf("%d %d", &u, &v), ans -= g[u][v]&1,  g[u][v]= 1;
    for (int i = 1 ; i <= n; ++ i) {
        for (int j = 1; j <= m; ++ j) {
            if (g[i][j]) continue;
            int id1 = (i-1)*m+1+j;
            if (i & 1) {
                Add_Edge(s, id1, 1), Add_Edge(id1, s, 0);
                for (int k = 0; k < 8; ++ k) {
                    int tx = i + dic[k][0], ty = j + dic[k][1];
                    int id2 = (tx-1)*m+ty+1;
                    if (!check(tx, ty))
                        Add_Edge(id1, id2, 1), Add_Edge(id2, id1, 0);
                }
            }
            else Add_Edge(id1, t, 1), Add_Edge(t, id1, 0);
        }
    }
    printf("%d\n", n*m-ans-Dinic()-k);
    return 0;
}
View Code

P3033 [USACO11NOV]Cow Steeplechase G

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, S, T, p;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline int read() {
    char c = getchar(); int x = 0, f = 1;
    for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}

inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline int dfs(int u, int flow){
    if(u == T) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

struct LINE
{
    int x1,y1, x2,y2;
};

bool judge(LINE a, LINE b)
{
    return b.x1<=a.x1&&a.x1<=b.x2&&a.y1<=b.y1&&b.y1<=a.y2;
}
int main() {
    init();
    scanf("%d", &n);
    S = 0, T = n+1;
    node_num = T;
    LINE A[2*n+1], B[2*n+1];
    int ca = 0, cb = 0;
    for (int i = 1, x1,x2,y1,y2; i <= n; ++ i) {
        scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
        if (x1 > x2) swap(x1, x2);
        if (y1 > y2) swap(y1, y2);
        if (x1 == x2) A[++ca] = {x1,y1,x2,y2};
        else B[++cb] = {x1,y1,x2,y2};
    }

    for (int i = 1; i <= ca; ++ i) Add_Edge(S, i, 1);
    for (int i = 1; i <= cb; ++ i) Add_Edge(i+ca, T, 1);
    for (int i = 1; i <= ca; ++ i)
        for (int j = 1; j <= cb; ++ j)
            if (judge(A[i], B[j]))
                Add_Edge(i, ca+j, 1);
    printf("%d\n", n - Dinic());
    return 0;
}
View Code

P4304 [TJOI2013]攻击装置

#include <bits/stdc++.h>
using namespace std;

const int maxn = 300+10;
const int maxm = 100000+100;
const int inf = 0x3f3f3f3f;

int n, m, r, c;
int tot, head[maxm];
int ans, linkx[maxm], linky[maxm];
int dx[maxm], dy[maxm];
char g[maxn][maxn];
int id[maxn][maxn];
int dir[8][2]={-1,2,-1,-2, 1,2,1,-2, 2,1, 2,-1, -2,1,-2,-1};
struct E {
    int to, next;
}edge[maxm]; //这里千万要注意,如果题目是双向边的话,这里的N要开2*N

inline void read(int& ans)
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch<'0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while ( ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    ans =  x * f;
}

inline void add_edge(int u, int v) {
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
    if (!linkx[u] && !linky[v])
        linky[v] = u, linkx[u] = v, ++ ans;
}

void init()
{
    tot = ans = 0;
    memset(head, -1, sizeof(head));
    memset(linkx, 0, sizeof(linkx));
    memset(linky, 0, sizeof(linky));
}

bool bfs()
{
    bool ret = 0;
    queue<int> q;
    memset(dx, 0, sizeof(dx)), memset(dy, 0, sizeof(dy));
    for (int i = 1; i <= n*n; ++ i)
        if (!linkx[i]) q.push(i);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (int i = head[u]; ~i; i = edge[i].next) {
            int v = edge[i].to;
            if (!dy[v]) {
                dy[v] = dx[u] + 1;
                if (!linky[v]) ret = 1;
                else dx[linky[v]] = dy[v]+1, q.push(linky[v]);
            }
        }
    }
    return ret;
}

bool dfs(int u)
{
    for (int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].to;
        if (dy[v] == dx[u] + 1) {
            dy[v] = 0;
            if (!linky[v] || dfs(linky[v])) {
                linky[v] = u, linkx[u] = v;
                return 1;
            }
        }
    }
    return 0;
}

int main()
{
    init();
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin >> n;
    for (int i = 1; i <= n; ++ i)
        for (int j = 1; j <= n; ++ j)
            cin >> g[i][j], id[i][j] = (i-1)*n+j;
    int res = 0;
    for (int i = 1; i <= n; ++ i) {
        for (int j = 1; j <= n; ++ j) {
            if (g[i][j] == '0') {
                ++ res;
                for (int k = 0; k < 8; ++ k) {
                    int tx = i + dir[k][0], ty = j+ dir[k][1];
                    if (ty < 1 || ty > n || tx < 1 || tx > n || g[tx][ty] == '1') continue;
                    add_edge(id[i][j], id[tx][ty]);
                }
            }
        }
    }
    while (bfs()) {
        for (int i = 1; i <= n*n; ++ i)
            if (!linkx[i] && dfs(i))
                ++ ans;
    }
    cout << res-ans/2 << endl;
  //这段循环是用来输出跟谁匹配
//    printf("%d\n", ans);
//    for (int i = 1; i <= n; ++ i)
//        if (linkx[i]) printf("%d %d\n",i, linkx[i]);
    return 0;
}
View Code

最小路径覆盖:

P2172 [国家集训队]部落战争

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, s, t, k, r, c;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;
}

inline bool Bfs(){
    for(int i = 1; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[s] = 0;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[t] != -1;
}

inline int dfs(int u, int flow){
    if(u == t) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 1; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(s, inf);
    }
    return ans;
}

char g[205][205];
int id[205][205];
int dir[2][2]={1,-1,1,1};
int main(){
    cnt = 0;
    memset(head, -1, sizeof(head));

    scanf("%d %d %d %d", &n, &m, &r, &c);
    s = 1, t = n*m*2+2;
    node_num = t;

    for (int i = 1; i <= n; ++ i)
        for (int j = 1; j <= m; ++ j)
            cin >> g[i][j], id[i][j] = (i-1)*m+j+1;
    int res = 0;
    for (int i = 1; i <= n; ++ i) {
        for (int j = 1; j <= m; ++ j) {
            Add_Edge(s, id[i][j], 1), Add_Edge(id[i][j], s, 0);
            Add_Edge(id[i][j] + n*m, t, 1), Add_Edge(t, id[i][j] + n*m, 0);
            if (g[i][j] == '.') {
                ++ res;
                for (int k = 0; k < 2; ++ k) {
                    int tx = i + r*dir[k][0], ty = j+ c*dir[k][1];
                    if (ty < 1 || ty > m || tx < 1 || tx > n || g[tx][ty] == 'x') continue;
                    Add_Edge(id[i][j], id[tx][ty]+n*m, 1), Add_Edge(id[tx][ty]+n*m, id[i][j], 0);
                }
                for (int k = 0; k < 2; ++ k) {
                    int tx = i + c*dir[k][0], ty = j + r*dir[k][1];
                    if (ty < 1 || ty > m || tx < 1 || tx > n || g[tx][ty] == 'x') continue;
                    Add_Edge(id[i][j], id[tx][ty]+n*m, 1), Add_Edge(id[tx][ty]+n*m, id[i][j], 0);
                }
            }
        }
    }

    printf("%d\n", res-Dinic());
    return 0;
}
View Code

P2764 最小路径覆盖问题

ha之前用二分图刷了就没用dinic了

#include <bits/stdc++.h>
using namespace std;

const int maxn = 501;
const int maxm = 250001;
const int inf = 0x3f3f3f3f;

int n,  m;
int tot, head[maxn];
int ans, linkx[maxn], linky[maxn];
int dx[maxn], dy[maxn];

struct E {
    int to, next;
}edge[maxm]; //这里千万要注意,如果题目是双向边的话,这里的N要开2*N

inline void read(int& ans)
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch<'0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while ( ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    ans =  x * f;
}
void inti()
{

}
//加边
inline void add_edge(int u, int v) {
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
    if (!linkx[u] && !linky[v])
        linky[v] = u, linkx[u] = v, ++ ans;
}

bool bfs()
{
    bool ret=0;
    queue<int> q;
    memset(dx, 0, sizeof(dx)), memset(dy, 0, sizeof(dy));
    for (int i = 1; i <= n; ++ i)
        if (!linkx[i]) q.push(i);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (int i = head[u]; ~i; i = edge[i].next) {
            int v = edge[i].to;
            if (!dy[v]) {
                dy[v] = dx[u] + 1;
                if (!linky[v]) ret = 1;
                else dx[linky[v]] = dy[v]+1, q.push(linky[v]);
            }
        }
    }
    return ret;
}

bool dfs(int u)
{
    for (int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].to;
        if (dy[v] == dx[u] + 1) {
            dy[v] = 0;
            if (!linky[v] || dfs(linky[v])) {
                linky[v] = u, linkx[u] = v;
                return 1;
            }
        }
    }
    return 0;
}

int main()
{
    int a, b;
    memset(head, -1, sizeof(head));
    read(n), read(m);
    for (int i = 1; i <= m; ++ i)
        read(a), read(b), add_edge(a,b);

    while(bfs()) {
        for (int i = 1; i <= n; ++ i)
            if (!linkx[i] && dfs(i))
                ++ ans;
    }
    bool vis[n+1] = {0};
    stack<int> s;
    for (int i = 1; i <= n; ++ i) {
        if (vis[i]) continue;
        int cnt = 0;
        int t = i;
        int res[n+1];
        vis[t] = 1;
        s.push(t);
        while (linky[t]) {
            t = linky[t];
            s.push(t);
            vis[t] = 1;
        }
        while (!s.empty()) {
            res[cnt++] = s.top();
            s.pop();
        }
        t = i;
        while (linkx[t]) {
            t = linkx[t];
            res[cnt++] = t;
            vis[t] = 1;
        }
        for (int i = 0; i < cnt-1; ++ i)
            cout << res[i] << " ";
        cout << res[cnt-1] << endl;
    }
    printf("%d\n",n-ans);
//  这段循环是用来输出跟谁匹配
//    for (int i = 1; i <= n; ++ i)
//        printf("%d ",linkx[i]);

    return 0;
}
View Code

二分答案 + 网络流check

P2857 [USACO06FEB]Steady Cow Assignment G

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
//const int maxm = 1e3 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int m,n,s,t, x, head[maxn], num_edge;
int cur[maxn],deep[maxn],last[maxn],num[maxn];
int node_num;
int cap[40];
int wanted[maxn][40];
//cur当前弧优化; last该点的上一条边; num桶 用来GAP优化
struct Edge{
    int next,to,dis;
}edge[maxn*2];

void add_edge(int from,int to,int dis)
{
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    edge[num_edge].next=head[from];
    head[from]=num_edge++;
}
//bfs仅用于更新deep
void bfs(int t)
{
    queue<int>q;
    for (int i=0; i<=node_num; i++) cur[i]=head[i];
    for (int i=1; i<=node_num; i++) deep[i]=node_num;
    deep[t]=0;
    q.push(t);
    while (!q.empty())
    {
        int now=q.front(); q.pop();
        for (int i=head[now]; i != -1; i=edge[i].next)
        {
            if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边
            {
                deep[edge[i].to] = deep[now]+1;
                q.push(edge[i].to);
            }
        }
    }
}

int add_flow(int s,int t)
{
    int ans=inf,now=t;
    while (now!=s)
    {
        ans=min(ans,edge[last[now]].dis);
        now=edge[last[now]^1].to;
    }
    now=t;
    while (now!=s)
    {
        edge[last[now]].dis-=ans;
        edge[last[now]^1].dis+=ans;
        now=edge[last[now]^1].to;
    }
    return ans;
}
int isap(int s,int t)
{
    int now=s;
    int maxflow = 0;
    bfs(t);//搜出一条增广路
    for (int i=1; i<=node_num; i++) num[deep[i]]++;
    while (deep[s]<node_num)
    {
        if (now==t)
        {//如果到达汇点就直接增广,重新回到源点进行下一轮增广
            maxflow+=add_flow(s,t);
            now=s;//回到源点
        }
        bool has_find=0;
        for (int i=cur[now]; i!=-1; i=edge[i].next)
        {
            if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路
            {
                has_find=true;
                cur[now]=i;//当前弧优化
                now=edge[i].to;
                last[edge[i].to]=i;
                break;
            }
        }
        if (!has_find)//没有找到出边,重新编号
        {
            int minn=node_num-1;
            for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径
                if (edge[i].dis)
                    minn=min(minn,deep[edge[i].to]);
            if ((--num[deep[now]])==0) break;//GAP优化 出现了断层
            num[deep[now]=minn+1]++;
            cur[now]=head[now];
            if (now!=s)
                now=edge[last[now]^1].to;
        }
    }
    return maxflow;
}

bool check(int range)
{
    for (int i = 1; i + range - 1 <= m; ++ i) {
        num_edge = 0;
        memset(head,-1,sizeof(head));
        for (int j = 1; j <= m; ++ j) {
            int barn = n+1+j;
            add_edge(barn, t, cap[j]), add_edge(t, barn, 0);
        }
        for (int j = 2; j <= n+1; ++ j) {
            add_edge(s, j, 1), add_edge(j, s, 0);
            for (int k = 0; k <= range-1; ++ k) {
                int barn = n+1+wanted[j][k+i];
                add_edge(j, barn, 1), add_edge(barn, j, 0);
            }
        }
        if (isap(s,t) == n) return 1;
    }
    return 0;
}


int main(){
    scanf("%d %d", &n, &m);
    node_num = n+m+2;
    s = 1, t = n+m+2;
    for (int i = 2; i <= n+1; ++ i)
        for (int j = 1; j <= m; ++ j)
            scanf("%d", &wanted[i][j]);
    for (int i = 1; i <= m; ++ i)
        scanf("%d", &cap[i]);
    //整数二分答案
    int left = 0, right = m, ans = 40;
    while(left <= right) {
        int mid = (left + right) / 2;
        if (check(mid)) {
            right = mid - 1;
            ans = min(ans, mid);
        }
        else
            left = mid + 1;
    }
    printf("%d\n", ans);

    return 0;
}
View Code

P2936 [USACO09JAN]Total Flow S

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
//const int maxm = 1e3 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int m,n,s,t, x, head[maxn], num_edge;
int cur[maxn],deep[maxn],last[maxn],num[maxn];
int node_num;
int cap[40];
int wanted[maxn][40];
//cur当前弧优化; last该点的上一条边; num桶 用来GAP优化
struct Edge{
    int next,to,dis;
}edge[maxn*2];

void add_edge(int from,int to,int dis)
{
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    edge[num_edge].next=head[from];
    head[from]=num_edge++;
}
//bfs仅用于更新deep
void bfs(int t)
{
    queue<int>q;
    for (int i=0; i<=node_num; i++) cur[i]=head[i];
    for (int i=1; i<=node_num; i++) deep[i]=node_num;
    deep[t]=0;
    q.push(t);
    while (!q.empty())
    {
        int now=q.front(); q.pop();
        for (int i=head[now]; i != -1; i=edge[i].next)
        {
            if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边
            {
                deep[edge[i].to] = deep[now]+1;
                q.push(edge[i].to);
            }
        }
    }
}

int add_flow(int s,int t)
{
    int ans=inf,now=t;
    while (now!=s)
    {
        ans=min(ans,edge[last[now]].dis);
        now=edge[last[now]^1].to;
    }
    now=t;
    while (now!=s)
    {
        edge[last[now]].dis-=ans;
        edge[last[now]^1].dis+=ans;
        now=edge[last[now]^1].to;
    }
    return ans;
}
int isap(int s,int t)
{
    int now=s;
    int maxflow = 0;
    bfs(t);//搜出一条增广路
    for (int i=1; i<=node_num; i++) num[deep[i]]++;
    while (deep[s]<node_num)
    {
        if (now==t)
        {//如果到达汇点就直接增广,重新回到源点进行下一轮增广
            maxflow+=add_flow(s,t);
            now=s;//回到源点
        }
        bool has_find=0;
        for (int i=cur[now]; i!=-1; i=edge[i].next)
        {
            if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路
            {
                has_find=true;
                cur[now]=i;//当前弧优化
                now=edge[i].to;
                last[edge[i].to]=i;
                break;
            }
        }
        if (!has_find)//没有找到出边,重新编号
        {
            int minn=node_num-1;
            for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径
                if (edge[i].dis)
                    minn=min(minn,deep[edge[i].to]);
            if ((--num[deep[now]])==0) break;//GAP优化 出现了断层
            num[deep[now]=minn+1]++;
            cur[now]=head[now];
            if (now!=s)
                now=edge[last[now]^1].to;
        }
    }
    return maxflow;
}

void init()
{
    num_edge = 0;
    memset(head,-1,sizeof(head));
}

int main()
{
    init();
    scanf("%d", &n);
    s = 1, t = 26;
    node_num = 100;
    for (int i = 1; i <= n; i++) {
        char tu, tv;
        int u, v, w;
        scanf("\n%c %c %d", &tu, &tv, &w);
        u = tu-64, v = tv-64;
        add_edge(u, v, w);
        add_edge(v, u, 0);//正反向建图
    }
    printf("%d\n", isap(s, t));
    return 0;
}
View Code

P3153 [CQOI2009]跳舞

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;

struct Edge{
    int to;
    int next;
    int w;
}edge[maxn*2];

int head[maxn];
int cur[maxn];//当前弧优化数组
int n, m, S, T, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
int node_num;
int rec[100+5][100+5];

inline void init(){
    cnt = 0;
    memset(head, -1, sizeof head);
}

inline int read() {
    char c = getchar(); int x = 0, f = 1;
    for ( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

inline void Add_Edge(int u, int v, int w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}

inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline int dfs(int u, int flow){
    if(u == T) return flow;
    int del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            int ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline int Dinic(){
    int ans = 0;
    while(Bfs()){
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

bool judge(int mid) {
    init();
    for (int i = 1; i <= n; ++ i) {
        Add_Edge(S, i, mid);
        Add_Edge(i, i+n, k);
        for (int j = 1; j <= n; ++j) {
            if (rec[i][j]) Add_Edge(i, j+2*n, 1);
            else Add_Edge(i+n, j+3*n, 1);
        }
    }
    for (int i = 1; i <= n; ++ i) {
        Add_Edge(i+3*n,i+2*n, k);
        Add_Edge(i+2*n, T, mid);
    }
    return Dinic() == mid * n;
}

int main() {
    init();
    scanf("%d %d", &n, &k);
    S = 0, T = 4*n+1;
    node_num = T;

    for (int i = 1; i <= n; ++ i) {
        for (int j = 1; j <= n; ++ j) {
            char temp;
            cin >> temp;
            if (temp == 'Y') rec[i][j] = 1;
        }
    }

    int left = 0, right = 100000;
    int ans = 0;
    while (left <= right) {
        int mid = (left + right) / 2;
        if (judge(mid)) {
            left = mid + 1;
            ans = max(ans, mid);
        }
        else
            right = mid - 1;
    }
    cout << ans << endl;
    return 0;
}
View Code

分层图网络流:

P2754 [CTSC1999]家园 / 星际转移问题

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 10;
//const int maxm = 1e3 * 2 + 10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int m,n,S,T,k, x, head[maxn], num_edge;
int cur[maxn],deep[maxn],last[maxn],num[maxn];
int node_num;
int cap[40];
int wanted[maxn][40];
//cur当前弧优化; last该点的上一条边; num桶 用来GAP优化
struct Edge{
    int next,to,dis;
}edge[maxn*2];

void add_edge(int from,int to,int dis)
{
    edge[num_edge].to=to;
    edge[num_edge].dis=dis;
    edge[num_edge].next=head[from];
    head[from]=num_edge++;

    edge[num_edge].to=from;
    edge[num_edge].dis=0;
    edge[num_edge].next=head[to];
    head[to]=num_edge++;
}
//bfs仅用于更新deep
void bfs(int T)
{
    queue<int>q;
    for (int i=0; i<=node_num; i++) cur[i]=head[i];
    for (int i=0; i<=node_num; i++) deep[i]=node_num;
    deep[T]=0;
    q.push(T);
    while (!q.empty())
    {
        int now=q.front(); q.pop();
        for (int i=head[now]; i != -1; i=edge[i].next)
        {
            if (deep[edge[i].to]==node_num && edge[i^1].dis)//i^1是为了找反边
            {
                deep[edge[i].to] = deep[now]+1;
                q.push(edge[i].to);
            }
        }
    }
}

int add_flow(int S,int T)
{
    int ans=inf,now=T;
    while (now!=S)
    {
        ans=min(ans,edge[last[now]].dis);
        now=edge[last[now]^1].to;
    }
    now=T;
    while (now!=S)
    {
        edge[last[now]].dis-=ans;
        edge[last[now]^1].dis+=ans;
        now=edge[last[now]^1].to;
    }
    return ans;
}

int isap(int S,int T)
{
    int now=S;
    int maxflow = 0;
    bfs(T);//搜出一条增广路
    for (int i=1; i<=node_num; i++) num[deep[i]]++;
    while (deep[S]<node_num)
    {
        if (now==T)
        {//如果到达汇点就直接增广,重新回到源点进行下一轮增广
            maxflow+=add_flow(S,T);
            now=S;//回到源点
        }
        bool has_find=0;
        for (int i=cur[now]; i!=-1; i=edge[i].next)
        {
            if (deep[now]==deep[edge[i].to]+1 && edge[i].dis)//找到一条增广路
            {
                has_find=true;
                cur[now]=i;//当前弧优化
                now=edge[i].to;
                last[edge[i].to]=i;
                break;
            }
        }
        if (!has_find)//没有找到出边,重新编号
        {
            int minn=node_num-1;
            for (int i=head[now]; i!=-1; i=edge[i].next)//回头找路径
                if (edge[i].dis)
                    minn=min(minn,deep[edge[i].to]);
            if ((--num[deep[now]])==0) break;//GAP优化 出现了断层
            num[deep[now]=minn+1]++;
            cur[now]=head[now];
            if (now!=S)
                now=edge[last[now]^1].to;
        }
    }
    return maxflow;
}

void init()
{
    num_edge = 0;
    memset(head,-1,sizeof(head));
}

int f[1050], p[1050], r[1050], s[1050][1050];

int Find(int x){
    if(f[x]==x) return x;
    else return f[x]=Find(f[x]);
}

void un(int a,int b){
    int fa=Find(a),fb=Find(b);
    if(fa!=fb) f[fa]=fb;
}


int main() {
    init();
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n+1; ++ i) f[i] = i;
    for (int i = 0; i < m; i++) {
        scanf("%d%d", &p[i], &r[i]);
        for (int j = 0; j < r[i]; j++) {
            scanf("%d", &s[i][j]);
            if (s[i][j] == -1) s[i][j] = n+1;
            if (j) un(s[i][j], s[i][j-1]);
        }
    }
    if (Find(0) != Find(n+1)) {
        printf("0\n");
    }
    else {
        n += 2;
        S = 1001, T = 1002;
        node_num = 1050;
        add_edge(S, 0, inf);
        add_edge(n-1, T, inf);
        for (int ans = 1; ; ans++) {
            add_edge(S, 0+ans*n, inf);//超级源点向这一时刻的地球连边
            add_edge(n-1+ans*n, T, inf);//这一时刻的月球向超级汇点连边
            for (int i = 1; i < n-1; i++)
                add_edge(i+(ans-1)*n, i+ans*n, inf);//上下层空间站不动
            for (int i = 0; i < m; i++) {
                int u = s[i][(ans-1)%r[i]]+(ans-1)*n;
                int v = s[i][ans%r[i]]+ans*n;
                add_edge(u, v, p[i]);//通过飞船移动
            }
            k -= isap(S,T);
            if (k <= 0) { printf("%d\n", ans); break; }
        }
    }
    return 0;
}
View Code

网络流与其他奇奇怪怪的结合:

最短路径图 + 最大流:

P3171 [CQOI2015]网络吞吐量

通过dijkstra跑出各点的dist,

之后通过判断 g[i][j] + dist[i] == dist[j]来判断该点是否在最短路径图上

之后建边,跑最大流

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define pii pair<long long, int>
typedef long long ll;

const int maxn = 1e6 + 10;
const int maxm = 1e5 * 2 + 10;
const ll inf = 1e18;
const int mod = 1e9 + 7;


struct Edge{
    int to;
    int next;
    ll w;
} edge[maxn*2];

int head[maxn], node_num;
int cur[maxn];//当前弧优化数组
int n, m, S, T, k;//点数,边数,源点,汇点
int dis[maxn];//Bfs深度
int cnt = 0;//边数
ll g[505][505];
ll diss[maxn];
bool vis[maxn];

inline void init(){
    cnt = 0;
    for (int i = 0; i <= node_num; ++ i)
        head[i] = -1, diss[i] = inf, vis[i] = 0;
    memset(g, -1, sizeof(g));
}

inline void Add_Edge(int u, int v, ll w){
    edge[cnt].next = head[u];
    edge[cnt].to = v;
    edge[cnt].w = w;
    head[u] = cnt++;

    edge[cnt].next = head[v];
    edge[cnt].to = u;
    edge[cnt].w = 0;
    head[v] = cnt++;
}


inline bool Bfs(){
    for(int i = 0; i <= node_num; ++i) dis[i] = -1; //这里要根据你所建的点数来确定
    dis[S] = 0;
    queue<int> q;
    q.push(S);
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next){
            int v = edge[i].to;
            if(dis[v] == -1 && edge[i].w){//没有标记深度并且有残量
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
    }
    return dis[T] != -1;
}

inline ll dfs(int u, ll flow){
    if(u == T) return flow;
    ll del = flow;
    for(int i = cur[u]; i != -1; i = edge[i].next){
        cur[u] = edge[i].next;//当前弧优化,下次直接从cur[u]开始增广,节省时间
        int v = edge[i].to;
        if (dis[v] == dis[u] + 1 && edge[i].w > 0){//深度+1且残量大于0
            ll ans = dfs(v, min(del, edge[i].w));//木桶原理
            edge[i].w -= ans;//正向弧减增广流量
            edge[i ^ 1].w += ans;//反向弧加增广流量
            del -= ans;//总流量减增广流量
            if(del == 0) break;//总流量为0则不继续增广
        }
    }
    return flow - del;//返回本次增广的流量
}

inline ll Dinic() {
    ll ans = 0;
    while(Bfs()) {
        for(int i = 0; i <= node_num; ++i) cur[i] = head[i];
        ans += dfs(S, inf);
    }
    return ans;
}

void dijkstra() {
    priority_queue<pii,vector<pii>,greater<pii> > q;//从小到大
    diss[1] = 0; q.push({diss[1],1});
    while (!q.empty()) {
        int now = q.top().second;
        q.pop();
        if(vis[now]) continue;
        vis[now] = 1;
        for(int v = 1; v <= n; ++ v) {
            if (g[now][v] != -1)
                if(diss[v] > diss[now] + g[now][v]) {
                    diss[v] = diss[now] + g[now][v];
                    q.push({diss[v],v});
                }
        }
    }
}

int main() {
    scanf("%d %d", &n, &m);
    S = 0, T = n+n+1;
    node_num = T;
    init();
    for (int i = 1, u, v; i <= m; ++ i) {
        ll w;
        scanf("%d %d %lld", &u, &v, &w);
        if (g[u][v] != -1) g[u][v] = g[v][u] = min(g[v][u], w);
        else  g[u][v] = g[v][u] = w;
    }
    dijkstra();
    ll rec[n+1];
    Add_Edge(S, 1, inf), Add_Edge(n*2, T, inf);
    for (int i = 1; i <= n; ++ i) {
        scanf("%lld", &rec[i]);
        if (i == 1 || i == n) Add_Edge(i, i+n, inf);
        else Add_Edge(i, i+n, rec[i]);
    }
    for (int u = 1; u <= n; ++ u) {
        for (int v = 1; v <= n; ++ v) {
            if (u == v) continue;
            if (g[u][v] != -1 && diss[v] == g[u][v] + diss[u])
                Add_Edge(u+n, v, inf);//, cout << u << " "<< v << endl;
        }
    }

    printf("%lld\n", Dinic());
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/Vikyanite/p/13418686.html