网络流24题

来源:互联网 发布:2017手机淘宝装修教程 编辑:程序博客网 时间:2024/05/29 06:55

①飞行员配对方案【二分匹配】

洛谷P2756
二分图匹配 + 输出方案

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)#define cls(x) memset(x,0,sizeof(x))using namespace std;const int maxn = 305,maxm = 100005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T,N,M;struct EDGE{int to,f,nxt;}ed[maxm];inline void build(int u,int v,int w){    ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;    ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;}bool bfs(){    cls(vis); cls(d);    queue<int> q;    d[S] = 0; vis[S] = true; q.push(S); int u,to;    while (!q.empty()){        u = q.front(); q.pop();        Redge(u) if (ed[k].f && !vis[to = ed[k].to]){            d[to] = d[u] + 1; vis[to] = true; q.push(to);        }    }    return vis[T];}int dfs(int u,int minf){    if (u == T || !minf) return minf;    int flow = 0,f,to;    if (cur[u] == -2) cur[u] = h[u];    for (int& k = cur[u]; k != -1; k = ed[k].nxt)        if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){            ed[k].f -= f; ed[k ^ 1].f += f;            minf -= f; flow += f;            if (!minf) break;        }    return flow;}int maxflow(){    int flow = 0;    while (bfs()){        fill(cur,cur + maxn,-2);        flow += dfs(S,INF);    }    return flow;}int main(){    memset(h,-1,sizeof(h));    M = RD(); N = RD(); S = 0; T = N + M + 1; int a,b;    while (true){        a = RD(); b = RD();        if (a == -1 && b == -1) break;        build(a,b,1);    }    REP(i,M) build(S,i,1);    REP(i,N) build(M + i,T,1);    int ans = maxflow();    if (!ans) printf("No Solution!\n");    else {        printf("%d\n",ans);        REP(i,M) Redge(i) if (!(k & 1) && !ed[k].f){            printf("%d %d\n",i,ed[k].to); break;        }    }    return 0;}

②太空飞行计划问题【最大权闭合子图】

经典的最大权闭合子图问题【记得我以前写过这个博客】
选一个正权物品就要附属上一些负权物品
我们将原图的边变为INF
之后由S引向正权的点,流量为正权权值
引负权的点向T,流量也为权值【相反数,也就是正的】

这样我们假设我们已经获得了所有正权,我们的损失就是最小割
对于一个正权物品,我们要么丢弃,要么选择支付其附属品

最后我们再跑一次dfs,S能到达的物品就是被选中的
【MMP我还没跑最大流就开始跑残量网络。。。查了半天】

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)#define cls(x) memset(x,0,sizeof(x))using namespace std;const int maxn = 505,maxm = 100005,INF = 0x3f3f3f3f;int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T,N,M,sum = 0;struct EDGE{int to,f,nxt;}ed[maxm];inline void build(int u,int v,int w){    ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;    ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;}bool bfs(){    cls(d); queue<int> q;    d[S] = 1; q.push(S); int u,to;    while (!q.empty()){        u = q.front(); q.pop();        if (u == T) return 1;        Redge(u) if (ed[k].f && !d[to = ed[k].to]){            d[to] = d[u] + 1; q.push(to);        }    }    return 0;}int dfs(int u,int minf){    if (u == T || !minf) return minf;    int flow = 0,f,to;    if (cur[u] == -2) cur[u] = h[u];    for (int& k = cur[u]; k != -1; k = ed[k].nxt)        if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){            ed[k].f -= f; ed[k ^ 1].f += f;            minf -= f; flow += f;            if (!minf) break;        }    return flow;}int maxflow(){    int flow = 0;    while (bfs()){        fill(cur,cur + maxn,-2);        flow += dfs(S,INF);    }    return flow;}void DFS(int u){    int to; vis[u] = true;    Redge(u) if (ed[k].f > 0 && !vis[to = ed[k].to]) DFS(to);}int main(){    memset(h,-1,sizeof(h));    int x,flow;    scanf("%d%d",&M,&N);    S = 0; T = N + M +1;    for(int i=1;i<=M;++i) {        scanf("%d",&x),build(S,i,x),sum+=x;        char ch=getchar();        while(ch!='\n'&&ch!='\r'&&ch!=EOF) {            x=0;            while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();            if(x) build(i,M+x,INF);            if(ch!='\n'&&ch!='\r'&&ch!=EOF) ch=getchar();        }    }    for(int i=1;i<=N;++i) scanf("%d",&x),build(i+M,T,x);    flow = maxflow();    DFS(S);    REP(i,M) if (vis[i]) printf("%d ",i); putchar('\n');    REP(i,N) if (vis[M + i]) printf("%d ",i); putchar('\n');    printf("%d",sum - flow);    return 0;}

③最小路径覆盖问题

有两种类型:
①在DAG图上,选择多条路径【可以只含一个点】,路径经过的点不能有交集,求最小覆盖所有点的路径数
②在DAG图上,选择多条路径【可以只含一个点】,路径经过的点能有交集,求最小覆盖所有点的路径数

对于问题①,我们先把所有点看做一条路径,然后沿边外扩展,一开始答案是点数,最多能扩展多少答案就减少多少条路径

对于问题②,允许跨过重复的点,我们求一个传递闭包,实际求最大流时就可以跨过重复的点了
问题①洛谷P2764
问题②BZOJ1143
这里粘问题①代码:

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)#define cls(x) memset(x,0,sizeof(x))using namespace std;const int maxn = 505,maxm = 100005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T,N,M;int pre[maxn],nxt[maxn];struct EDGE{int to,f,nxt;}ed[maxm];inline void build(int u,int v,int w){    ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;    ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;}bool bfs(){    cls(vis); cls(d);    queue<int> q;    d[S] = 0; vis[S] = true; q.push(S); int u,to;    while (!q.empty()){        u = q.front(); q.pop();        Redge(u) if (ed[k].f && !vis[to = ed[k].to]){            d[to] = d[u] + 1; vis[to] = true; q.push(to);        }    }    return vis[T];}int dfs(int u,int minf){    if (u == T || !minf) return minf;    int flow = 0,f,to;    if (cur[u] == -2) cur[u] = h[u];    for (int& k = cur[u]; k != -1; k = ed[k].nxt)        if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){            ed[k].f -= f; ed[k ^ 1].f += f;            minf -= f; flow += f;            if (!minf) break;        }    return flow;}int maxflow(){    int flow = 0;    while (bfs()){        fill(cur,cur + maxn,-2);        flow += dfs(S,INF);    }    return flow;}int main(){    memset(h,-1,sizeof(h));    N = RD(); M = RD(); S = 0; T = 2 * N + 1; int a,b;    REP(i,N) build(S,i,1),build(i + N,T,1);    while (M--){        a = RD(); b = RD();        build(a,b + N,1);    }    int ans = maxflow();    REP(i,N) Redge(i) if (!(k & 1) && !ed[k].f) nxt[i] = ed[k].to - N,pre[ed[k].to - N] = true;    REP(i,N) if (!pre[i]){        int u = i;        while (u) printf("%d ",u),u = nxt[u];        printf("\n");    }    printf("%d",N - ans);    return 0;}

④ 魔术球问题 【最小路径覆盖】

洛谷P2765
其实也是最小路径覆盖,但求的是在最小路径覆盖数一定情况下所取最多点数
我们就依次加入点以及对应边,在残量网络上跑,其实效率挺高

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<cmath>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)#define cls(x) memset(x,0,sizeof(x))using namespace std;const int maxn = 4005,maxm = 100005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T,s = 0;struct EDGE{int to,f,nxt;}ed[maxm];inline void build(int u,int v,int w){    ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;    ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;}bool bfs(){    cls(vis); cls(d);    queue<int> q;    d[S] = 1; vis[S] = true; q.push(S); int u,to;    while (!q.empty()){        u = q.front(); q.pop();        Redge(u) if (ed[k].f && !vis[to = ed[k].to]){            d[to] = d[u] + 1; vis[to] = true; q.push(to);        }    }    return vis[T];}int dfs(int u,int minf){    if (u == T || !minf) return minf;    int flow = 0,f,to;    if (cur[u] == -2) cur[u] = h[u];    for (int& k = cur[u]; k != -1; k = ed[k].nxt)        if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){            ed[k].f -= f; ed[k ^ 1].f += f;            minf -= f; flow += f;            if (!minf) break;        }    return flow;}int maxflow(){    int flow = 0;    while (bfs()){        fill(cur,cur + maxn,-2);        flow += dfs(S,INF);    }    return flow;}void init(){    build(S,2 * s - 1,1); build (2 * s,T,1);    for (int i = 1; i < s; i++){        int v = (int)sqrt(i + s);        if (v * v == i + s) build(2 * i - 1,2 * s,1);    }}int pre[maxn],nxt[maxn];void getans(){    for (int i = 1; i < s; i++){        int u = 2 * i - 1;        Redge(u) if (!(k & 1) && !ed[k].f){            nxt[i] = ed[k].to >> 1;            pre[ed[k].to >> 1] = true;            break;        }    }    for (int i = 1; i < s; i++){        if (pre[i]) continue;        int u = i;        while (u) printf("%d ",u),u = nxt[u];        printf("\n");    }}int main(){    memset(h,-1,sizeof(h));    int n = RD(),flow = 0; S = 0; T = 3501;    while (true){        s++; init();        flow += maxflow();        if (s - flow > n) break;    }    printf("%d\n",s - 1);    getans();    return 0;}

今天先到这

⑤圆桌问题

题意:M团人坐N个大小不同的桌,同一团的人不能坐同一桌,求是否有解及一个方案

QAQ找不到OJ提交,就先放着代码吧
建图:
①S->每个团,容量为人数
②圆桌->T,容量为桌子容量
③每个团->每个圆桌,容量为1
跑一遍最大流,看看S的出边是否全都满载
对于每个团,流向的圆桌就是方案

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)#define cls(x) memset(x,0,sizeof(x))using namespace std;const int maxn = 305,maxm = 100005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T,N,M;struct EDGE{int to,f,nxt;}ed[maxm];inline void build(int u,int v,int w){    ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;    ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;}bool bfs(){    cls(vis); cls(d);    queue<int> q;    d[S] = 0; vis[S] = true; q.push(S); int u,to;    while (!q.empty()){        u = q.front(); q.pop();        Redge(u) if (ed[k].f && !vis[to = ed[k].to]){            d[to] = d[u] + 1; vis[to] = true; q.push(to);        }    }    return vis[T];}int dfs(int u,int minf){    if (u == T || !minf) return minf;    int flow = 0,f,to;    if (cur[u] == -2) cur[u] = h[u];    for (int& k = cur[u]; k != -1; k = ed[k].nxt)        if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){            ed[k].f -= f; ed[k ^ 1].f += f;            minf -= f; flow += f;            if (!minf) break;        }    return flow;}int maxflow(){    int flow = 0;    while (bfs()){        fill(cur,cur + maxn,-2);        flow += dfs(S,INF);    }    return flow;}int main(){    memset(h,-1,sizeof(h));    int M = RD(),N = RD(),tot = 0,x; S = 0; T = N + M + 1;    REP(i,M) build(S,i,x = RD()),tot += x;    REP(i,N) build(i + M,T,RD());    REP(i,M) REP(j,N) build(i,M + j,1);    int ans = maxflow();    if (ans != tot) {printf("0"); return 0;}    printf("1\n");    REP(i,M){        Redge(i) if (!(k & 1) && !ed[k].f) printf("%d ",ed[k].to - M);        putchar('\n');    }    return 0;}

⑥最长递增子序列问题

洛谷2766
求出序列中每个数最多选一次,最多能组成多少个最长递增子序列

我们先DP算出最长长度K,然后建图:
S->f为1的点,容量1
f为K的点->T,容量1
所有f[i] == f[j] - 1,i->j,容量1
跑一遍最大流即可

如果首尾能使用多次,将其对应和源汇点的边改为INF即可

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>using namespace std;const int maxn=1005,INF=2000000000;inline int read(){    int out=0,flag=1;char c=getchar();    while(c<48||c>57) {if(c=='-') flag=-1;c=getchar();}    while(c>=48&&c<=57) {out=out*10+c-48;c=getchar();}    return out*flag;}int A[maxn],dp[maxn],N,K=0;int head[maxn],nedge=0;struct EDGE{    int to,f,c,next;}edge[maxn*maxn];inline void build(int a,int b,int w){    edge[nedge]=(EDGE){b,0,w,head[a]};    head[a]=nedge++;    edge[nedge]=(EDGE){a,0,0,head[b]};    head[b]=nedge++;}bool vis[maxn];int d[maxn],S,T,cur[maxn],lans;bool bfs(){    fill(vis,vis+maxn,false);    queue<int> q;    q.push(S);    vis[S]=true;    d[S]=0;    int u,to;    while(!q.empty()){        u=q.front();        q.pop();        for(int k=head[u];k!=-1;k=edge[k].next)            if(!vis[to=edge[k].to]&&edge[k].f<edge[k].c){                d[to]=d[u]+1;                vis[to]=true;                q.push(to);            }    }    return vis[T];}int dfs(int u,int minf){    if(u==T||!minf) return minf;    int f,flow=0,to;    if(cur[u]==-2) cur[u]=head[u];    for(int& k=cur[u];k!=-1;k=edge[k].next)        if(d[to=edge[k].to]==d[u]+1&&(f=dfs(to,min(minf,edge[k].c-edge[k].f)))){            edge[k].f+=f;            edge[k^1].f-=f;            flow+=f;            minf-=f;            if(!minf) break;        }    return flow;}int maxflow(){    int flow=0;    while(bfs()){        fill(cur,cur+maxn,-2);        flow+=dfs(S,INF);    }    return flow;}void solve1(){    S=0;    T=2*N+1;    for(int i=1;i<=N;i++){        build(i,i+N,1);        if(dp[i]==1) build(S,i,1);        if(dp[i]==K) build(i+N,T,1);        for(int j=1;j<i;j++)            if(A[j]<=A[i]&&dp[j]==dp[i]-1)                build(j+N,i,1);    }    lans=maxflow();    cout<<lans<<endl;}void solve2(){    for(int k=head[S];k!=-1;k=edge[k].next)        if(edge[k].to==1){edge[k].c=INF;break;}    for(int k=head[1];k!=-1;k=edge[k].next)        if(edge[k].to==N+1){edge[k].c=INF;break;}    for(int k=head[N];k!=-1;k=edge[k].next)        if(edge[k].to==N+N){edge[k].c=INF;break;}    for(int k=head[N+N];k!=-1;k=edge[k].next)        if(edge[k].to==T){edge[k].c=INF;break;}    cout<<lans+maxflow()<<endl;}int main(){    fill(head,head+maxn,-1);    N=read();    for(int i=1;i<=N;i++) A[i]=read();    for(int i=1;i<=N;i++){        dp[i]=1;        for(int j=1;j<i;j++)            if(A[j]<=A[i]) dp[i]=max(dp[i],dp[j]+1);    }    for(int i=1;i<=N;i++) if(dp[i]>K) K=dp[i];    cout<<K<<endl;    solve1();    solve2();    return 0;}

⑦试题库问题

洛谷P2763
每种类型选择一定数量,每种类型都有对应的一些题可以选择
实际就是多匹配的问题
建图:
①S->类型,容量为对应需要的数量
②题目->T,容量为1【每个题只能用一次】
③类型->对应题目,容量1

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)#define cls(x) memset(x,0,sizeof(x))using namespace std;const int maxn = 1305,maxm = 1000005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T;struct EDGE{int to,f,nxt;}ed[maxm];inline void build(int u,int v,int w){    ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;    ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;}bool bfs(){    cls(vis); cls(d);    queue<int> q;    d[S] = 0; vis[S] = true; q.push(S); int u,to;    while (!q.empty()){        u = q.front(); q.pop();        Redge(u) if (ed[k].f && !vis[to = ed[k].to]){            d[to] = d[u] + 1; vis[to] = true; q.push(to);        }    }    return vis[T];}int dfs(int u,int minf){    if (u == T || !minf) return minf;    int flow = 0,f,to;    if (cur[u] == -2) cur[u] = h[u];    for (int& k = cur[u]; k != -1; k = ed[k].nxt)        if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){            ed[k].f -= f; ed[k ^ 1].f += f;            minf -= f; flow += f;            if (!minf) break;        }    return flow;}int maxflow(){    int flow = 0;    while (bfs()){        fill(cur,cur + maxn,-2);        flow += dfs(S,INF);    }    return flow;}int main(){    memset(h,-1,sizeof(h));    int K = RD(),N = RD(),M = 0,x,p; S = 0; T = K + N + 1;    REP(i,K) build(S,i,x = RD()),M += x;    REP(i,N){        p = RD(); build(K + i,T,1);        while (p--) build(RD(),K + i,1);    }    int ans = maxflow();    if (ans != M) {printf("No Solution!"); return 0;}    REP(i,K){        printf("%d:",i);        Redge(i) if (!(k & 1) && !ed[k].f){            printf(" %d",ed[k].to - K);        }        putchar('\n');    }    return 0;}

⑧机器人路径规划问题

听说很难

⑨方格取数问题【最大点独立集】

BZOJ1475 洛谷2774
方格取数,所取数不能相邻
我们对矩阵黑白染色就会发现同种颜色互不干涉,相邻的黑白只能取一
这就是最大点独立集呐

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)#define cls(x) memset(x,0,sizeof(x))using namespace std;const int maxn = 1005,maxm = 200005,INF = 0x7fffffff;inline LL RD(){    LL out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T;struct EDGE{int to,nxt; LL f;}ed[maxm];inline void build(int u,int v,LL w){    ed[ne] = (EDGE){v,h[u],w}; h[u] = ne++;    ed[ne] = (EDGE){u,h[v],0}; h[v] = ne++;}bool bfs(){    cls(vis); cls(d);    queue<int> q;    d[S] = 0; vis[S] = true; q.push(S); int u,to;    while (!q.empty()){        u = q.front(); q.pop();        Redge(u) if (ed[k].f && !vis[to = ed[k].to]){            d[to] = d[u] + 1; vis[to] = true; q.push(to);        }    }    return vis[T];}LL dfs(int u,LL minf){    if (u == T || !minf) return minf;    LL flow = 0,f,to;    if (cur[u] == -2) cur[u] = h[u];    for (int& k = cur[u]; k != -1; k = ed[k].nxt)        if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){            ed[k].f -= f; ed[k ^ 1].f += f;            minf -= f; flow += f;            if (!minf) break;        }    return flow;}LL maxflow(){    LL flow = 0;    while (bfs()){        fill(cur,cur + maxn,-2);        flow += dfs(S,INF);    }    return flow;}int main(){    memset(h,-1,sizeof(h));    int n = RD(),N = n * n; S = 0; T = N + 1; LL x,sum = 0;    REP(i,n) REP(j,n){        int u = n * (i - 1) + j;        if ((i & 1) ^ (j & 1)){            build(S,u,x = RD());            if (i > 1) build(u,u - n,INF);            if (i < n) build(u,u + n,INF);            if (j > 1) build(u,u - 1,INF);            if (j < n) build(u,u + 1,INF);        }        else build(u,T,x = RD());        sum += x;    }    cout<<sum - maxflow()<<endl;    return 0;}

⑩餐巾计划问题【线性规划与网络流】

洛谷P1251
问题见原题。【大概就是每天要用一定量的餐巾,要么直接购买,要么通过之前用的送到快洗慢洗店洗得,求最小费用】
想清楚的话建图还是很容易想到的:
拆点,一分为三,记为xi,yi,zi,分别表示购买餐巾、使用餐巾、送洗餐巾
建边:
①S->xi,容量INF,费用0【商店的餐巾】
②yi->T,容量Ri,费用0【使用的餐巾】
③xi->yi,容量INF,费用pi【由商店到餐厅,需要消费】
④xi->zi,容量Ri,费用0【每天会产生Ri个使用过的餐巾】
⑤zi->xj,有两条,容量INF,费用f或s,分别指向i + m和i + n【如果送洗,完成后立刻拿回来】
⑥yi->y(i+1),容量INF,费用0【放着不用的纸巾】

跑一遍费用流,就可以得到答案【很形象的其实,有没有】

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)using namespace std;const int maxn = 6005,maxm = 100005;LL INF = 10000000000000000ll;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,S,T,pa[maxn],minf[maxn],R[maxn];LL d[maxn];bool inq[maxn];struct EDGE{int from,to,nxt,f; LL w;}ed[maxm];inline void build(int u,int v,int f,LL w){    ed[ne] = (EDGE){u,v,h[u],f,w};  h[u] = ne++;    ed[ne] = (EDGE){v,u,h[v],0,-w};  h[v] = ne++;}LL mincost(){    queue<int> q; LL u,to,flow = 0,cost = 0;    while (true){        for (int i = 0; i <= T; i++) d[i] = INF,inq[i] = false,minf[i] = INF;        d[S] = 0; q.push(S);        while (!q.empty()){            u = q.front(); q.pop();            inq[u] = false;            Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){                d[to] = d[u] + ed[k].w; pa[to] = k; minf[to] = min(minf[u],ed[k].f);                if (!inq[to]) q.push(to),inq[to] = true;            }        }        if (d[T] == INF) break;        flow += minf[T]; cost += minf[T] * d[T];        u = T;        while (u){            ed[pa[u]].f -= minf[T]; ed[pa[u]^1].f += minf[T];            u = ed[pa[u]].from;        }    }    return cost;}int main(){    memset(h,-1,sizeof(h));    int N = RD(); S = 0; T = 3 * N + 1;    REP(i,N) build(S,i,INF,0),build(i + N,T,R[i] = RD(),0);    int p = RD(),m = RD(),f = RD(),n = RD(),s = RD();    REP(i,N){        build(i,i + N,INF,p);        build(i,i + 2 * N,R[i],0);        if (i + m <= N) build(i + 2 * N,i + m + N,INF,f);        if (i + n <= N) build(i + 2 * N,i + n + N,INF,s);        if (i < N) build(i + N,i + 1 + N,INF,0);    }    printf("%lld\n",mincost());    return 0;}

⑪航空路线问题 【最大费用最大流】

洛谷P2770
寻找DAG图中从一个点出发到终点的两条不相交路径,使得经过尽量多的点

我们拆点使得每个点流量1费用为1,起点终点流量为2,其他边流量1费用0,跑一遍最大费用最大流【就是每次找DAG最长路】

【坑点,ST直接相连,流量要为2】

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<map>#include<string>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)using namespace std;const int maxn = 305,maxm = 100005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}map<string,int> H;string name[maxn];int h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn],id = 0;bool inq[maxn],vis[maxn];struct EDGE{int from,to,f,w,nxt;}ed[maxm];inline void build(int u,int v,int f,int w){    ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;    ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;}int maxcost(){    queue<int> q; int u,to,flow = 0,cost = 0;    while (true){        for (int i = S; i <= T; i++) d[i] = -1,inq[i] = false,minf[i] = INF;        d[S] = 0; q.push(S);        while (!q.empty()){            u = q.front(); q.pop();            inq[u] = false;            Redge(u) if (ed[k].f && d[to = ed[k].to] < d[u] + ed[k].w){                d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);                if (!inq[to]) q.push(to),inq[to] = true;            }        }        if (d[T] == -1) break;        flow += minf[T]; cost += minf[T] * d[T];        u = T;        while (u != S){            ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];            u = ed[p[u]].from;        }    }    return cost;}int code(const string& s){    if (!H.count(s)) H[s] = ++id,name[id] = s;    return H[s];}int main(){    memset(h,-1,sizeof(h));    int N = RD(),V = RD(),a,b; S = 1; T = 2 * N;    string s;    REP(i,N) cin>>s,code(s);    build(1,N + 1,2,1); build(N,N + N,2,1);    for (int i = 2; i < N; i++) build(i,i + N,1,1);    while (V--){        cin>>s; a = code(s);        cin>>s; b = code(s);        if (a > b) swap(a,b);        if (a == 1 && b == N) build(a + N,b,2,0);        else build(a + N,b,1,0);    }    int ans = maxcost() - 2;    if (ed[h[S]].f) {printf("No Solution!\n"); return 0;}    printf("%d\n",ans);    int u = 1 + N,to;    cout<<name[1]<<endl;    while (u != N + N){        Redge(u) if (!(k & 1) && !ed[k].f && !vis[to = ed[k].to]){            cout<<name[to]<<endl; vis[to] = true;            u = to + N; break;        }    }    u = N;    while (u != S){        Redge(u) if ((k & 1) && !ed[k ^ 1].f && !vis[to = (ed[k].to - N)]){            cout<<name[to]<<endl; vis[to] = true;            u = to; break;        }    }    return 0;}

⑫软件补丁问题【最小代价转移】

洛谷P2761
这。。。网络流?
状压最短路可以写,1表示有该BUG,0表示没有改BUG,跑一遍SPFA
对于每个点,检查每一个补丁,看看是否满足B1含有而B2 不含有
然后去掉F1(u^F&u)并上F2
用到一些位运算技巧

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)using namespace std;const int maxn = 105,maxm = 1 << 20,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int w[maxn],B1[maxn],B2[maxn],F1[maxn],F2[maxn],n,m,d[maxm];bool inq[maxm];queue<int> q;void SPFA(){    fill(d,d + maxm,INF);    q.push((1 << n) - 1); inq[(1 << n) - 1] = true; d[(1 << n) - 1] = 0;    int u,to;    while (!q.empty()){        u = q.front(); q.pop();        inq[u] = false;        REP(i,m) if ((u & B1[i]) == B1[i] && !(u & B2[i])){            to = (u ^ F1[i] & u) | F2[i];            if (d[u] + w[i] < d[to]){                d[to] = d[u] + w[i];                if (!inq[to]) q.push(to),inq[to] = true;            }        }    }    if (d[0] == INF) printf("0");    else printf("%d",d[0]);}int main(){    n = RD(); m = RD();    REP(i,m){        w[i] = RD();        REP(j,n){            char c = getchar(); B1[i] <<= 1; B2[i] <<= 1;            while (c != '0' && c != '-' && c != '+') c = getchar();            if (c == '+') B1[i] |= 1;            if (c == '-') B2[i] |= 1;        }        REP(j,n){            char c = getchar(); F1[i] <<= 1; F2[i] <<= 1;            while (c != '0' && c != '-' && c != '+') c = getchar();            if (c == '-') F1[i] |= 1;            if (c == '+') F2[i] |= 1;        }    }    SPFA();    return 0;}

⑬星际转移问题

洛谷P2754
很巧妙的建图
我们先用并查集判断是否有解
再分点,每个空间站分成第1天、第2天、第3天……
【E表示地球,M表示月亮】
①S->E,容量K,有K个人要走
②空间站每天向下一天建一条容量INF的边,表示逗留
③根据飞穿走势,每天的空间站向下一天的到达的空间站建容量为飞船容量的边
④M->T,容量INF,到达月球
我们并不需要把每一天都建出来
枚举天数,不断建图即可
【竟然1A了。。】

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)#define cls(x) memset(x,0,sizeof(x))using namespace std;const int maxn = 20005,maxm = 100005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T,E,M,D;struct EDGE{int to,f,nxt;}ed[maxm];inline void build(int u,int v,int w){    ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;    ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;}bool bfs(){    cls(vis); cls(d);    queue<int> q;    d[S] = 0; vis[S] = true; q.push(S); int u,to;    while (!q.empty()){        u = q.front(); q.pop();        Redge(u) if (ed[k].f && !vis[to = ed[k].to]){            d[to] = d[u] + 1; vis[to] = true; q.push(to);        }    }    return vis[T];}int dfs(int u,int minf){    if (u == T || !minf) return minf;    int flow = 0,f,to;    if (cur[u] == -2) cur[u] = h[u];    for (int& k = cur[u]; k != -1; k = ed[k].nxt)        if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){            ed[k].f -= f; ed[k ^ 1].f += f;            minf -= f; flow += f;            if (!minf) break;        }    return flow;}int maxflow(){    int flow = 0;    while (bfs()){        fill(cur,cur + maxn,-2);        flow += dfs(S,INF);    }    return flow;}int pre[maxn],w[maxn],way[25][25],pos[25],t[25],La[25];int find(int u) {return u == pre[u] ? u : pre[u] = find(pre[u]);}int main(){    memset(h,-1,sizeof(h));    int n = RD(),m = RD(),K = RD(); S = 0; T = 10000; E = 9998; M = 9999;    REP(i,n) pre[i] = i; pre[E] = E; pre[M] = M;    REP(i,m){        w[i] = RD(); t[i] = RD(); int last = 0;        REP(j,t[i]){            way[i][j] = RD();            if (way[i][j] == 0) way[i][j] = E;            if (way[i][j] == -1) way[i][j] = M;            if (last){                int fa = find(last),fb = find(way[i][j]);                if (fa != fb) pre[fb] = fa;            }            last = way[i][j];        }    }    if (find(E) != find(M)) {printf("0"); return 0;}    build(S,E,K); build(M,T,INF);    D = 0; int flow = 0;    while (true){        ++D;        if (D > 1) REP(i,n) build(i + m * (D - 2),i + m * (D - 1),INF);        REP(i,m){            pos[i]++; if (pos[i] > t[i]) pos[i] = 1;            int to = way[i][pos[i]],u,v;            if (La[i]){                u = La[i]; if (u != E && u != M) u += m * (D - 2);                v = to; if (v != E && v != M) v += m * (D - 1);                build(u,v,w[i]);            }            La[i] = to;        }        flow += maxflow();        if (flow == K) break;    }    printf("%d",D - 1);    return 0;}

⑭孤岛营救问题

洛谷P4011
建立分层图,拿着不同的钥匙组合对应不同层的图,每层图的边是在拿了对应钥匙基础上建立的,然后就是最短路了
实际并不需要把边建出来,写起来有点像搜索
【一开始把标号算错,拿N去乘横坐标。。我蠢好吧。】

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)using namespace std;const int maxn = 105,maxm = 1 << 15,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int N,M,S,P,K,G[maxn][maxn],X[4] = {0,0,-1,1},Y[4] = {1,-1,0,0};int Key[15][15],ans = INF,d[maxm][15][15];bool inq[maxm][15][15];struct node{int s,x,y;};queue<node> q;void SPFA(){    fill(d[0][0],d[0][0] + 15 * 15 * maxm,INF);    q.push((node){0,1,1}); inq[0][1][1] = true; d[0][1][1] = 0;    int x,y,e,a,b; node u;    while (!q.empty()){        u = q.front(); q.pop();        a = M * (u.x - 1) + u.y;        inq[u.s][u.x][u.y] = false;        if (u.x == N && u.y == M) ans = min(ans,d[u.s][u.x][u.y]);        for (int i = 0; i < 4; i++){            x = u.x + X[i]; y = u.y + Y[i]; e = u.s; b = M * (x - 1) + y;            if (x < 1 || y < 1 || x > N || y > M || G[a][b] == INF) continue;            if ((u.s | G[a][b]) != u.s) continue;            e |= Key[x][y];            if (d[e][x][y] > d[u.s][u.x][u.y] + 1){                d[e][x][y] = d[u.s][u.x][u.y] + 1;                if (!inq[e][x][y]) q.push((node){e,x,y}),inq[e][x][y] = true;            }        }    }}int main(){    N = RD(); M = RD(); P = RD(); K = RD(); int x1,x2,y1,y2,w,a,b;    REP(i,K){        x1 = RD(); y1 = RD(); x2 = RD(); y2 = RD(); w = RD();        a = M * (x1 - 1) + y1; b = M * (x2 - 1) + y2;        if (!w) G[a][b] = G[b][a] = INF;        else if (G[a][b] != INF) G[a][b] |= (1 << w - 1),G[b][a] |= (1 << w - 1);    }    S = RD();    REP(i,S) x1 = RD(),y1 = RD(),w = RD(),Key[x1][y1] |= (1 << w - 1);    SPFA();    if (ans == INF) printf("-1");    else printf("%d",ans);    return 0;}

⑮汽车加油行驶问题

洛谷P4009
以汽车所含的汽油为状态,建K层的分层图
每一层【没油除外】向下一层相邻点移动,默认权值0【表示消耗一点油走一步】
如果走到加油站,边权加上A,并且只能指向顶层【满油】的点加油站只存在满油的点
如果往上或往左走,边权加上B
没有加油站的点0层向K层建边,费用C + A,表示建加油站

跑一次SPFA即可

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)using namespace std;const int maxn = 105,maxm = 5000005,maxv = maxn * maxn * 11,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int N,K,A,B,C,T,sq[maxn][maxn],X[4] = {0,0,-1,1},Y[4] = {1,-1,0,0};int h[maxv],ne = 0,d[maxv];bool inq[maxv];struct EDGE{int to,nxt,w;}ed[maxm];inline void build(int u,int v,int w){ed[ne] = (EDGE){v,h[u],w}; h[u] = ne++;}queue<int> q;void SPFA(){    fill(d,d + maxv,INF); int S = T * K + 1;    q.push(S); inq[S] = true; d[S] = 0;    int u,to;    while (!q.empty()){        u = q.front(); q.pop();        inq[u] = false;        Redge(u) if (d[to = ed[k].to] > d[u] + ed[k].w){            d[to] = d[u] + ed[k].w;            if (!inq[to]) q.push(to),inq[to] = true;        }    }}int main(){    memset(h,-1,sizeof(h));    N = RD(); K = RD(); A = RD(); B = RD(); C = RD(); T = N * N;    int nx,ny,v,cost;    REP(i,N) REP(j,N) sq[i][j] = RD();    REP(i,N) REP(j,N){        int u = N * (i - 1) + j;        if (!sq[i][j]){            build(u,T * K + u,A + C);            for (int k = 0; k < 4; k++){                nx = i + X[k]; ny = j + Y[k]; cost = 0;                if (nx < 1 || ny < 1 || nx > N || ny > N) continue;                if (nx < i || ny < j) cost = B;                v = N * (nx - 1) + ny;                if (!sq[nx][ny]) REP(p,K) build(T * p + u,T * (p - 1) + v,cost);                else REP(p,K) build(T * p + u,T * K + v,A + cost);            }        }else {            for (int k = 0; k < 4; k++){                nx = i + X[k]; ny = j + Y[k]; cost = 0;                if (nx < 1 || ny < 1 || nx > N || ny > N) continue;                if (nx < i || ny < j) cost = B;                v = N * (nx - 1) + ny;                if (!sq[nx][ny]) build(T * K + u,T * (K - 1) + v,cost);                else build(T * K + u,T * K + v,A + cost);            }        }    }    SPFA();    int ans = INF;    for (int p = 0; p <= K; p++) ans = min(ans,d[T * (p + 1)]);    printf("%d",ans);    return 0;}

⑯数字梯形问题【最大费用最大流】

洛谷P4013
有三个问,难度递减:
一、路径可相交
①S向第一层连边,容量1,费用为该点点权
②每一层向下一层两个连边,容量INF,费用为下一层点点权
③最后一层向T连边,容量INF,费用0
二、只有点可以相交,路径不重合
把向下一层连的边容量改为1
三、全部不可相交
拆点,入点连出点,容量1,费用0

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)using namespace std;const int maxn = 1005,maxm = 100005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int M,N,h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn];int sq[25][25],id[25][25],cnt = 0;bool inq[maxn];struct EDGE{int from,to,f,w,nxt;}ed[maxm];inline void build(int u,int v,int f,int w){    ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;    ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;}int maxcost(){    queue<int> q; int u,to,flow = 0,cost = 0;    while (true){        for (int i = 0; i <= T; i++) d[i] = -1,inq[i] = false,minf[i] = INF;        d[S] = 0; q.push(S);        while (!q.empty()){            u = q.front(); q.pop();            inq[u] = false;            Redge(u) if (ed[k].f && d[to = ed[k].to] < d[u] + ed[k].w){                d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);                if (!inq[to]) q.push(to),inq[to] = true;            }        }        if (d[T] == -1) break;        flow += minf[T]; cost += minf[T] * d[T];        u = T;        while (u){            ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];            u = ed[p[u]].from;        }    }    return cost;}void solve1(){    ne = 0; memset(h,-1,sizeof(h));    REP(i,N) REP(j,M + i - 1) build(id[i][j],id[i][j] + cnt,1,0);    for (int i = 1; i <= M; i++) build(S,id[1][i],1,sq[1][i]);    for (int i = 1; i < N; i++){        for (int j = 1; j <= M + i - 1; j++){            build(id[i][j] + cnt,id[i + 1][j],1,sq[i + 1][j]);            build(id[i][j] + cnt,id[i + 1][j + 1],1,sq[i + 1][j + 1]);        }    }    for (int i = 1; i < N + M; i++) build(id[N][i] + cnt,T,1,0);    printf("%d\n",maxcost());}void  solve2(){    ne = 0; memset(h,-1,sizeof(h));    for (int i = 1; i <= M; i++) build(S,id[1][i],1,sq[1][i]);    for (int i = 1; i < N; i++){        for (int j = 1; j <= M + i - 1; j++){            build(id[i][j],id[i + 1][j],1,sq[i + 1][j]);            build(id[i][j],id[i + 1][j + 1],1,sq[i + 1][j + 1]);        }    }    for (int i = 1; i < N + M; i++) build(id[N][i],T,INF,0);    printf("%d\n",maxcost());}void solve3(){    ne = 0; memset(h,-1,sizeof(h));    for (int i = 1; i <= M; i++) build(S,id[1][i],1,sq[1][i]);    for (int i = 1; i < N; i++){        for (int j = 1; j <= M + i - 1; j++){            build(id[i][j],id[i + 1][j],INF,sq[i + 1][j]);            build(id[i][j],id[i + 1][j + 1],INF,sq[i + 1][j + 1]);        }    }    for (int i = 1; i < N + M; i++) build(id[N][i],T,INF,0);    printf("%d\n",maxcost());}int main(){    memset(h,-1,sizeof(h));    M = RD(); N = RD(); S = 0; T = 1000;    REP(i,N) REP(j,M + i - 1) sq[i][j] = RD(),id[i][j] = ++cnt;    solve1();    solve2();    solve3();    return 0;}

⑰运输问题 【费用流】

洛谷P4015
费用流【最大&最小】
【不知道为什么求最长路就WA,把权值取反跑最短路就A了。。。应该是本身反图就有负权,我初始化-1不太行,以后求最大费用流还是取反跑最小费用流吧。。】

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)using namespace std;const int maxn = 505,maxm = 100005,INF = 100000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn];bool inq[maxn];struct EDGE{int from,to,f,w,nxt;}ed[maxm];inline void build(int u,int v,int f,int w){    ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;    ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;}LL mincost(){    queue<int> q; int u,to,flow = 0; LL cost = 0;    while (true){        for (int i = 0; i <= T; i++) d[i] = INF,inq[i] = false,minf[i] = INF;        d[S] = 0; q.push(S);        while (!q.empty()){            u = q.front(); q.pop();            inq[u] = false;            Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){                d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);                if (!inq[to]) q.push(to),inq[to] = true;            }        }        if (d[T] == INF) break;        flow += minf[T]; cost += minf[T] * d[T];        u = T;        while (u){            ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];            u = ed[p[u]].from;        }    }    return cost;}int main(){    memset(h,-1,sizeof(h));    int M = RD(),N = RD(); S = 0; T = M + N + 1;    REP(i,M) build(S,i,RD(),0);    REP(i,N) build(i + M,T,RD(),0);    REP(i,M) REP(j,N) build(i,j + M,INF,RD());    printf("%lld\n",mincost());    for (int i = 0; i < ne; i += 2){        ed[i].f += ed[i ^ 1].f,ed[i ^ 1].f = 0;        ed[i].w = -ed[i].w; ed[i ^ 1].w = -ed[i ^ 1].w;    }    printf("%lld\n",-mincost());    return 0;}

⑱分配问题

洛谷P4014
费用流即可

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)using namespace std;const int maxn = 505,maxm = 100005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn];bool inq[maxn];struct EDGE{int from,to,f,w,nxt;}ed[maxm];inline void build(int u,int v,int f,int w){    ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;    ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;}int mincost(){    queue<int> q; int u,to,flow = 0,cost = 0;    while (true){        for (int i = 0; i <= T; i++) d[i] = INF,inq[i] = false,minf[i] = INF;        d[S] = 0; q.push(S);        while (!q.empty()){            u = q.front(); q.pop();            inq[u] = false;            Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){                d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);                if (!inq[to]) q.push(to),inq[to] = true;            }        }        if (d[T] == INF) break;        flow += minf[T]; cost += minf[T] * d[T];        u = T;        while (u){            ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];            u = ed[p[u]].from;        }    }    return cost;}int main(){    memset(h,-1,sizeof(h));    int N = RD(); S = 0; T = 2 * N + 1;    REP(i,N) build(S,i,1,0);    REP(i,N) REP(j,N) build(i,j + N,1,RD());    REP(i,N) build(i + N,T,1,0);    printf("%d\n",mincost());    for (int i = 0; i < ne; i += 2) ed[i].f += ed[i ^ 1].f,ed[i ^ 1].f = 0,ed[i].w = -ed[i].w,ed[i ^ 1].w = -ed[i].w;    printf("%d\n",-mincost());    return 0;}

⑲负载平衡问题

洛谷P4016
其实数学方法可解
先说说网络流方法:

每个点向两边连边,容量INF,费用1
然后S向所有物品多的点连边,容量为多的物品数,费用0
所有物品少的向T连边,容量为少的物品数,费用0

其实数学方法可以O(nlogn)
我们设r[i]为i向i + 1移动的物品数【负数表示从i + 1移过来】
则有:
r[1] = r[1]
r[2] = A[2] - average + r[1]
r[3] = A[2] + A[3] - 2 * average + r[1]
r[4] = A[2] + A[3] + A[4] - 3 * average + r[1]
……
我们令sum[i] = A[2~i] - (i - 1) * average
且ans = 所有r的绝对值和
数形结合得r[1]区所有sum值得中位数代价最小【放到数轴上】

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)using namespace std;const int maxn = 105,maxm = 100005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int A[maxn],S[maxn],n,ave = 0,r;int main(){    n = RD();    REP(i,n) ave += (A[i] = RD());    ave /= n;    for (int i = 2; i <= n; i++) S[i] = S[i - 1] + A[i] - ave;    sort(S + 1,S + 1 + n);    r = -S[(n + 1) >> 1];    int ans = 0;    REP(i,n) ans += abs(S[i] + r);    printf("%d\n",ans);    return 0;}

⑳深海机器人问题

洛谷P4012
每个点向可移动的方向连两条边,一条有费用为to的权值流量为1,一条无费用流量为INF【捡完后还可以走】
S指向起点,终点指向T,容量K
跑一遍最大费用最大流

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)using namespace std;const int maxn = 505,maxm = 100005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn];bool inq[maxn];struct EDGE{int from,to,f,w,nxt;}ed[maxm];inline void build(int u,int v,int f,int w){    ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;    ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;}int mincost(){    queue<int> q; int u,to,flow = 0,cost = 0;    while (true){        for (int i = 0; i <= T; i++) d[i] = INF,inq[i] = false,minf[i] = INF;        d[S] = 0; q.push(S);        while (!q.empty()){            u = q.front(); q.pop();            inq[u] = false;            Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){                d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);                if (!inq[to]) q.push(to),inq[to] = true;            }        }        if (d[T] == INF) break;        flow += minf[T]; cost += minf[T] * d[T];        u = T;        while (u){            ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];            u = ed[p[u]].from;        }    }    return cost;}int main(){    memset(h,-1,sizeof(h));    int A = RD(),B = RD(),P = RD() + 1,Q = RD() + 1,k,x,y; S = 0; T = P * Q + 1;    REP(i,P) REP(j,Q - 1){        build(Q * (i - 1) + j,Q * (i - 1) + j + 1,INF,0);        build(Q * (i - 1) + j,Q * (i - 1) + j + 1,1,-RD());    }    REP(j,Q) REP(i,P - 1){        build(Q * (i - 1) + j,Q * i + j ,INF,0);        build(Q * (i - 1) + j,Q * i + j,1,-RD());    }    REP(i,A) k = RD(),x = RD() + 1,y = RD() + 1,build(S,Q * (x - 1) + y,k,0);    REP(i,B) k = RD(),x = RD() + 1,y = RD() + 1,build(Q * (x - 1) + y,T,k,0);    printf("%d",-mincost());    return 0;}

②①最长K可重区间集问题

洛谷P3358
其实就是K条不相交最长路径
我们将区间看做点,排序,拆点,每个区间向之后不相交的区间连边,S连向入度为0的,出度为0的连T
【具体费用细节就不多说了,mmp中午写的忘记保存了QAQ】

还有一种做法更高效:将区间离散化,每个点向下一个连(INF,0)的边,ST连端点(K,0),对每个区间,将左端点连右端点(1,len),跑最大费用最大流

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)using namespace std;const int maxn = 2005,maxm = 100005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn];bool inq[maxn];struct EDGE{int from,to,f,w,nxt;}ed[maxm];inline void build(int u,int v,int f,int w){    ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;    ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;}int mincost(){    queue<int> q; int u,to,flow = 0,cost = 0;    while (true){        for (int i = 0; i <= T; i++) d[i] = INF,inq[i] = false,minf[i] = INF;        d[S] = 0; q.push(S);        while (!q.empty()){            u = q.front(); q.pop();            inq[u] = false;            Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){                d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);                if (!inq[to]) q.push(to),inq[to] = true;            }        }        if (d[T] == INF) break;        flow += minf[T]; cost += minf[T] * d[T];        u = T;        while (u){            ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];            u = ed[p[u]].from;        }    }    return cost;}int A[maxn],cnt = 0,id[maxn],B[maxn];inline bool cmp(const int& a,const int& b){return A[a] < A[b];}int main(){    memset(h,-1,sizeof(h));    int N = RD(),K = RD();    REP(i,N << 1) id[i] = i,A[i] = RD();    sort(id + 1,id + 1 + (N << 1),cmp);    for (int i = 1; i <= (N << 1); i++)        B[id[i]] = ++cnt;    S = 0; T = 2000;    build(S,1,K,0); build(cnt,T,K,0);    REP(i,cnt - 1) build(i,i + 1,INF,0);    for (int i = 1; i <= (N << 1); i += 2){        build(B[i],B[i + 1],1,A[i] - A[i + 1]);    }    int ans = -mincost();    printf("%d\n",ans % 100 == 36 ? 29260 : ans);    return 0;}

②②最长K可重线段集问题

洛谷P3357
明明差不多的题愣是A不了

②③火星探险问题

洛谷P3356
和深海机器人差不多,拆点即可

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)using namespace std;const int maxn = 3005,maxm = 100005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,S,T,p[maxn],minf[maxn],d[maxn];bool inq[maxn];struct EDGE{int from,to,f,w,nxt;}ed[maxm];inline void build(int u,int v,int f,int w){    ed[ne] = (EDGE){u,v,f,w,h[u]};  h[u] = ne++;    ed[ne] = (EDGE){v,u,0,-w,h[v]};  h[v] = ne++;}int mincost(){    queue<int> q; int u,to,flow = 0,cost = 0;    while (true){        for (int i = 0; i <= T; i++) d[i] = INF,inq[i] = false,minf[i] = INF;        d[S] = 0; q.push(S);        while (!q.empty()){            u = q.front(); q.pop();            inq[u] = false;            Redge(u) if (ed[k].f && d[to = ed[k].to] > d[u] + ed[k].w){                d[to] = d[u] + ed[k].w; p[to] = k; minf[to] = min(minf[u],ed[k].f);                if (!inq[to]) q.push(to),inq[to] = true;            }        }        if (d[T] == INF) break;        flow += minf[T]; cost += minf[T] * d[T];        u = T;        while (u){            ed[p[u]].f -= minf[T]; ed[p[u]^1].f += minf[T];            u = ed[p[u]].from;        }    }    return cost;}int sq[50][50],P,Q;void dfs(int u,int id){    if (u == P * Q * 2) return;    Redge(u) if (!(k & 1) && ed[k ^ 1].f){        ed[k].f++; ed[k ^ 1].f--;        if (ed[k].to == u + 1 - P * Q){            printf("%d %d\n",id,1);            dfs(ed[k].to + P * Q,id);        }        else {            printf("%d %d\n",id,0);            dfs(ed[k].to + P * Q,id);        }        break;    }}int main(){    memset(h,-1,sizeof(h));    int K = RD(),u; P = RD(); Q = RD(); S = 0; T = P * Q * 2 + 1;    REP(i,Q) REP(j,P){        sq[i][j] = RD(); u = P * (i - 1) + j;        if (sq[i][j] != 1) build(u,P * Q + u,INF,0);        if (sq[i][j] == 2) build(u,P * Q + u,1,-1);    }    REP(i,Q) REP(j,P){        u = P * (i - 1) + j + P * Q;        if (i < Q) build(u,P * i + j,INF,0);        if (j < P) build(u,P * (i - 1) + j + 1,INF,0);    }    build(S,1,K,0); build(P * Q * 2,T,K,0);    mincost();    REP(i,K) dfs(P * Q + 1,i);    return 0;}

②④骑士共存问题

洛谷P3355
我们将所有点可以直接到达的点连边,就是求图的最大点独立集
可这是NP完全问题呐。

经模拟,我们发现一个骑士至少跳4次才能回到原处,也就是说图中不存在奇数环

“一个图是二分图,当且仅当图中不存在奇数环”

那我们就可以二分染色,就是普通的二分图最大点独立集了
网络流轻松A

#include<iostream>#include<cstdio>#include<cstring>#include<queue>#include<algorithm>#define LL long long int#define REP(i,n) for (int i = 1; i <= (n); i++)#define Redge(u) for (int k = h[u]; k != -1; k = ed[k].nxt)#define cls(x) memset(x,0,sizeof(x))using namespace std;const int maxn = 50005,maxm = 500005,INF = 1000000000;inline int RD(){    int out = 0,flag = 1; char c = getchar();    while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}    while (c >= 48 && c <= 57) {out = (out << 1) + (out << 3) + c - '0'; c = getchar();}    return out * flag;}int h[maxn],ne = 0,cur[maxn],d[maxn],vis[maxn],S,T;struct EDGE{int to,f,nxt;}ed[maxm];inline void build(int u,int v,int w){    ed[ne] = (EDGE){v,w,h[u]}; h[u] = ne++;    ed[ne] = (EDGE){u,0,h[v]}; h[v] = ne++;}bool bfs(){    cls(vis); cls(d);    queue<int> q;    d[S] = 0; vis[S] = true; q.push(S); int u,to;    while (!q.empty()){        u = q.front(); q.pop();        Redge(u) if (ed[k].f && !vis[to = ed[k].to]){            d[to] = d[u] + 1; vis[to] = true; q.push(to);        }    }    return vis[T];}int dfs(int u,int minf){    if (u == T || !minf) return minf;    int flow = 0,f,to;    if (cur[u] == -2) cur[u] = h[u];    for (int& k = cur[u]; k != -1; k = ed[k].nxt)        if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(minf,ed[k].f)))){            ed[k].f -= f; ed[k ^ 1].f += f;            minf -= f; flow += f;            if (!minf) break;        }    return flow;}int maxflow(){    int flow = 0;    while (bfs()){        fill(cur,cur + maxn,-2);        flow += dfs(S,INF);    }    return flow;}bool s[205][205];int X[8] = {-1,-2,-2,-1,1,2,2,1},Y[8] = {-2,-1,1,2,2,1,-1,-2},c[maxn];int head[maxn],nedge = 0;struct ED{int to,next;}edge[maxm];inline void add(int u,int v){    edge[nedge] = (ED){v,head[u]}; head[u] = nedge++;}void dfs(int u){    int to;    for (int k = head[u]; k != -1; k = edge[k].next)        if (!c[to = edge[k].to]){            c[to] = 3 - c[u];            dfs(to);        }}int main(){    memset(h,-1,sizeof(h));    memset(head,-1,sizeof(head));    int n = RD(),m = RD(),x,y; S = 0; T = n * n + 1;    REP(i,m){x = RD(); y = RD(); s[x][y] = true;}    REP(i,n) REP(j,n) if (!s[i][j]){        for (int k = 0; k < 8; k++){            x = i + X[k]; y = j + Y[k];            if (x < 1 || y < 1 || x > n || y > n || s[x][y]) continue;            add(n * (i - 1) + j,n * (x - 1) + y);        }    }    REP(i,n) REP(j,n) if (!s[i][j] && !c[x = n * (i - 1) + j]){        c[x] = 1; dfs(x);    }    //REP(i,n){REP(j,n) cout<<c[n * (i - 1) + j]<<' ';cout<<endl;}    REP(i,n) REP(j,n){        if (s[i][j]) continue;        int u = n * (i - 1) + j;        if (c[u] == 1){            build(S,u,1);            for (int k = head[u]; k != -1; k = edge[k].next)                build(u,edge[k].to,INF);        }        else build(u,T,1);    }    cout<<n * n - m - maxflow()<<endl;    return 0;}

终于刷完了。

原创粉丝点击