- P3381 【模板】最小费用最大流
- P4016 负载平衡问题
- P4452 [国家集训队]航班安排
- P2045 方格取数加强版
- P2050 [NOI2012]美食节
- P2053 [SCOI2007]修车
- P2604 [ZJOI2010]网络扩容
- P2770 航空路线问题
- P3159 [CQOI2012]交换棋子
- P3356 火星探险问题
- P3358 最长k可重区间集问题
- P4013 数字梯形问题
- P4015 运输问题
- P5331 [SNOI2019]通信
1.P3381 【模板】最小费用最大流
类 d i n i c dinic dinic的 s p f a spfa spfa费用流,多路增广效率较高(然而平时用的是 E K EK EK)
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f,maxn=5e3+5,maxm=5e4+5;
int n,m,s,t,cost,maxflow,vis[maxn],dis[maxn];
struct edge{
int v,flow,cost,rev;
};
vector<edge>e[maxm*2];
bool spfa(){
memset(vis,0,sizeof(vis));
memset(dis,inf,sizeof(dis));
dis[s]=0;
vis[s]=1;
queue<int>q;
q.push(s);
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=0;i<e[u].size();i++){
int v=e[u][i].v;
int c=e[u][i].cost;
if(dis[v]>dis[u]+c&&e[u][i].flow){
dis[v]=dis[u]+c;
if(vis[v]==0){
q.push(v);
vis[v]=1;
}
}
}
}
if(dis[t]!=inf)return true;
return false;
}
int dfs(int u,int flow){
if(u==t){
vis[t]=1;
maxflow+=flow;
return flow;
}
int used=0;
vis[u]=1;
for(int i=0;i<e[u].size();i++){
int v=e[u][i].v;
int c=e[u][i].cost;
if((vis[v]==0||v==t)&&e[u][i].flow!=0&&dis[v]==dis[u]+c){
int minflow=dfs(v,min(flow-used,e[u][i].flow));
if(minflow!=0){
cost+=c*minflow;
e[u][i].flow-=minflow;
e[v][e[u][i].rev].flow+=minflow;
used+=minflow;
if(used==flow)break;
}
}
}
return used;
}
int mcmf(){
while(spfa()){
vis[t]=1;
while(vis[t]){
memset(vis,0,sizeof(vis));
dfs(s,inf);
}
}
return maxflow;
}
int main(){
scanf("%d%d%d%d",&n,&m,&s,&t);
int u,v,f,w;
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&u,&v,&f,&w);
e[u].push_back((edge){v,f,w,e[v].size()});
e[v].push_back((edge){u,0,-w,e[u].size()-1});
}
mcmf();
printf("%d %d",maxflow,cost);
return 0;
}
2.P4016 负载平衡问题
源点向每个点连一条边,流量为货量,费用 0 0 0
每个点向旁边的点连边,流量为 i n f inf inf, 费用 1 1 1
每个点向汇点连一条边,流量为 a v e r a g e average average,费用 0 0 0
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=0x3f3f3f3f,maxn=1005,maxm=100005;
struct edge{
int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m;
inline void add_edge(int u,int v,int cost,int flow){
e[++cnt].v=v;
e[cnt].cost=cost;
e[cnt].flow=flow;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].cost=-cost;
e[cnt].flow=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
int inque[maxn];
int dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){
queue<int>q;
memset(dis,0x3f,sizeof dis);
memset(flow,0x3f,sizeof flow);
memset(inque,0,sizeof inque);
q.push(s);
inque[s]=1;
dis[s]=0;
pre[t]=-1;
while(!q.empty()){
int u=q.front();
q.pop();
inque[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].cost&&e[i].flow){
dis[v]=dis[u]+e[i].cost;
pre[v]=u;
last[v]=i;
flow[v]=min(flow[u],e[i].flow);
if(!inque[v]){
q.push(v);
inque[v]=1;
}
}
}
}
if(pre[t]!=-1)return true;
return false;
}
int maxflow,mincost;
void mcmf(int s,int t){
maxflow=0;
mincost=0;
while(spfa(s,t)){
int u=t;
// printf("maxflow==%d %d %d\n",maxflow,flow[t],flow[t]*dis[t]);
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while(u!=s){
e[last[u]].flow-=flow[t];
e[last[u]^1].flow+=flow[t];
u=pre[u];
}
}
return;
}
signed main(){
cin>>n;
int s=n+1;
int t=n+2;
int x=0,sum=0;
for(int i=1;i<=n;i++){
cin>>x;
sum+=x;
add_edge(s,i,0,x);
if(i!=n){
add_edge(i,i+1,1,inf);
}
else{
add_edge(n,1,1,inf);
}
if(i!=1){
add_edge(i,i-1,1,inf);
}
else{
add_edge(1,n,1,inf);
}
}
int av=sum/n;
for(int i=1;i<=n;i++){
add_edge(i,t,0,av);
}
mcmf(s,t);
cout<<mincost;
return 0;
}
3.P4452 [国家集训队]航班安排
对于每趟航班:
拆点 费用 − q -q −q
到达时间 t + t+ t+飞回来时间小于 T T T的,跟汇点连边
出发时间晚于 从出发点到此机场的时间,源点向其连边
枚举每一趟航班,如果第
i
i
i趟的结束时间
+
+
+
i
i
i到
j
j
j的飞行时间
<
j
<j
<j的出发时间,
i
i
i和
j
j
j连边
(费用全为
f
f
f,流量为
i
n
f
inf
inf,源点流出的流量为
k
k
k)
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f,maxn=20005,maxm=400005;
struct edge{
int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k,T;
inline void add_edge(int u,int v,int cost,int flow){
e[++cnt].v=v;
e[cnt].cost=cost;
e[cnt].flow=flow;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].cost=-cost;
e[cnt].flow=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){
queue<int>q;
memset(dis,0x3f,sizeof dis);
memset(flow,0x3f,sizeof flow);
memset(inque,0,sizeof inque);
q.push(s);
inque[s]=1;
dis[s]=0;
pre[t]=-1;
while(!q.empty()){
int u=q.front();
q.pop();
inque[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].cost&&e[i].flow){
dis[v]=dis[u]+e[i].cost;
pre[v]=u;
last[v]=i;
flow[v]=min(flow[u],e[i].flow);
if(!inque[v]){
q.push(v);
inque[v]=1;
}
}
}
}
if(pre[t]!=-1)return true;
return false;
}
inline pair<int,int> mcmf(int s,int t){
int maxflow=0;
int mincost=0;
while(spfa(s,t)){
int u=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while(u!=s){
e[last[u]].flow-=flow[t];
e[last[u]^1].flow+=flow[t];
u=pre[u];
}
}
return {maxflow,mincost};
}
int t[205][205],f[205][205];
struct node{
int a,b,s,t,c;
}q[505];
int main(){
cin>>n>>m>>k>>T;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
scanf("%d",&t[i][j]);
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
scanf("%d",&f[i][j]);
}
}
for(int i=0;i<m;i++){
scanf("%d%d%d%d%d",&q[i].a,&q[i].b,&q[i].s,&q[i].t,&q[i].c);
}
int tt=2*m+1;
int st=2*m+2;
int ed=2*m+3;
for(int i=0;i<m;i++){
add_edge(i,i+m,-q[i].c,1);
if(t[q[i].b][0]+q[i].t<=T){
add_edge(i+m,ed,f[q[i].b][0],inf);
}
else continue;
if(q[i].s>=t[0][q[i].a]){
add_edge(tt,i,f[0][q[i].a],inf);
}
for(int j=0;j<m;j++){
if(t[q[i].b][q[j].a]+q[i].t<=q[j].s){
add_edge(i+m,j,f[q[i].b][q[j].a],inf);
}
}
}
add_edge(st,tt,0,k);
pii yaoyao=mcmf(st,ed);
cout<<-yaoyao.second;
return 0;
}
4.P2045 方格取数加强版
每个点和右&下的点相连,流量为inf,费用0
拆点,一条费用为 − - −点权,流量为 1 1 1,另外一条费用 0 0 0,流量 i n f inf inf
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=0x3f3f3f3f,maxn=100005,maxm=400005;
struct edge{
int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k;
inline void add_edge(int u,int v,int cost,int flow){
e[++cnt].v=v;
e[cnt].cost=cost;
e[cnt].flow=flow;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].cost=-cost;
e[cnt].flow=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
int inque[maxn];
int dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){
queue<int>q;
memset(dis,0x3f,sizeof dis);
memset(flow,0x3f,sizeof flow);
memset(inque,0,sizeof inque);
q.push(s);
inque[s]=1;
dis[s]=0;
pre[t]=-1;
while(!q.empty()){
int u=q.front();
q.pop();
inque[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].cost&&e[i].flow){
dis[v]=dis[u]+e[i].cost;
pre[v]=u;
last[v]=i;
flow[v]=min(flow[u],e[i].flow);
if(!inque[v]){
q.push(v);
inque[v]=1;
}
}
}
}
if(pre[t]!=-1)return true;
return false;
}
int maxflow,mincost;
void mcmf(int s,int t){
while(spfa(s,t)){
int u=t;
// printf("maxflow==%d %d %d\n",maxflow,flow[t],flow[t]*dis[t]);
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while(u!=s){
e[last[u]].flow-=flow[t];
e[last[u]^1].flow+=flow[t];
u=pre[u];
}
}
return;
}
inline int id(int x,int y){
return (x-1)*n+y;
}
signed main(){
cin>>n>>k;
m=n*n;
int m2=2*m+n;
int s=m*4+1;
int t=m*4+2;
int c;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>c;
add_edge(id(i,j),id(i,j)+m,-c,1);
add_edge(id(i,j),id(i,j)+m2,0,inf);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(j+1<=n){
add_edge(id(i,j)+m,id(i,j+1),0,inf);
add_edge(id(i,j)+m2,id(i,j+1),0,inf);
}
if(i+1<=n){
add_edge(id(i,j)+m,id(i+1,j),0,inf);
add_edge(id(i,j)+m2,id(i+1,j),0,inf);
}
}
}
add_edge(s,id(1,1),0,k);
add_edge(id(n,n)+m,t,0,k);
add_edge(id(n,n)+m2,t,0,k);
mcmf(s,t);
cout<<-mincost;
return 0;
}
5.P2050 [NOI2012]美食节
核心:动态加边
构建一个二分图,菜在左边,厨师在右边
将所有的菜和对应初始连边,费用为做这道菜的时间,流量为1
在跑费用流的过程中,记录是哪个厨师被征用了,并记录这是这个厨师做的第几道菜,每条边的费用为边权 ∗ t i m e *time ∗time,流量为1,
为什么这样是对的呢?其实这里是一个逆向思维,先做的菜等的,所有人等时间就是加倍的,所以时间 最久的菜应该要放到最后做才是最优的,优先选了做的时间少的菜,但是又会疑惑了(比如我,想了一晚上):先选的菜花的单位时间少,为什么还放在后面选,让后面做的时间久的菜等的时间翻倍?这个疑惑还是因为对网络流的理解不够,因为网络流是会自带修正的(反向边的作用),所以加上所有的边之后,最终跑出来的费用是会重新选择用时最短的菜 ∗ n *n ∗n,次短 ∗ n − 1 *n-1 ∗n−1,这样依此类推
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f,maxn=3005,maxm=400005;
struct edge{
int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1,tim[maxn],now,sum;
int n,m,k;
inline void add_edge(int u,int v,int cost,int flow){
e[++cnt].v=v;
e[cnt].cost=cost;
e[cnt].flow=flow;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].cost=-cost;
e[cnt].flow=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){
queue<int>q;
memset(dis,0x3f,sizeof dis);
memset(flow,0x3f,sizeof flow);
memset(inque,0,sizeof inque);
q.push(s);
inque[s]=1;
dis[s]=0;
pre[t]=-1;
while(!q.empty()){
int u=q.front();
q.pop();
inque[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].cost&&e[i].flow){
dis[v]=dis[u]+e[i].cost;
pre[v]=u;
last[v]=i;
flow[v]=min(flow[u],e[i].flow);
if(!inque[v]){
q.push(v);
inque[v]=1;
}
}
}
}
if(pre[t]!=-1)return true;
return false;
}
int mp[maxn][maxn],p[maxn];
int jl[maxn];
inline pair<int,int> mcmf(int s,int t){
int maxflow=0;
int mincost=0;
while(spfa(s,t)){
int u=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
int j=jl[pre[t]];
// printf("j==%d\n",j);
tim[j]++;
now++;
for(int i=1;i<=n;i++){
add_edge(i,now,mp[i][j]*tim[j],1);
}
add_edge(now,t,0,1);
jl[now]=j;
while(u!=s){
e[last[u]].flow-=flow[t];
e[last[u]^1].flow+=flow[t];
u=pre[u];
}
}
return {maxflow,mincost};
}
inline int id(int x,int y){
return (x-1)*sum+y+sum;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>p[i];
sum+=p[i];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>mp[i][j];
}
}
int num=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
jl[j+sum]=j;
add_edge(i,j+sum,mp[i][j],1);
}
}
int s=sum+m+1;
int t=sum+m+2;
now=t;
for(int i=1;i<=n;i++){
add_edge(s,i,0,p[i]);
}
for(int i=1;i<=m;i++){
tim[i]=1;
add_edge(i+sum,t,0,1);
}
pair<int,int>yaoyao=mcmf(s,t);
cout<<yaoyao.second;
return 0;
}
6.P2053 [SCOI2007]修车
上一题的暴力版,直接枚举所有厨师的所有时间即可
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f,maxn=3005,maxm=400005;
struct edge{
int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k;
inline void add_edge(int u,int v,int cost,int flow){
e[++cnt].v=v;
e[cnt].cost=cost;
e[cnt].flow=flow;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].cost=-cost;
e[cnt].flow=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){
queue<int>q;
memset(dis,0x3f,sizeof dis);
memset(flow,0x3f,sizeof flow);
memset(inque,0,sizeof inque);
q.push(s);
inque[s]=1;
dis[s]=0;
pre[t]=-1;
while(!q.empty()){
int u=q.front();
q.pop();
inque[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].cost&&e[i].flow){
dis[v]=dis[u]+e[i].cost;
pre[v]=u;
last[v]=i;
flow[v]=min(flow[u],e[i].flow);
if(!inque[v]){
q.push(v);
inque[v]=1;
}
}
}
}
if(pre[t]!=-1)return true;
return false;
}
inline pair<int,int> mcmf(int s,int t){
int maxflow=0;
int mincost=0;
while(spfa(s,t)){
int u=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while(u!=s){
e[last[u]].flow-=flow[t];
e[last[u]^1].flow+=flow[t];
u=pre[u];
}
}
return {maxflow,mincost};
}
int mp[maxn][maxn];
inline int id(int x,int y){
return (x-1)*n+y+n;
}
int main(){
cin>>m>>n;
int s=n+n*m+1;
int t=s+1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>mp[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=1;k<=n;k++){
add_edge(i,id(j,k),mp[i][j]*k,1);
}
}
}
for(int i=1;i<=n;i++){
add_edge(s,i,0,1);
}
for(int i=1;i<=n*m;i++){
add_edge(i+n,t,0,1);
}
pair<int,int>yaoyao=mcmf(s,t);
printf("%.2f",(double)yaoyao.second/n);
return 0;
}
7.P2604 [ZJOI2010]网络扩容
先所有边费用为 0 0 0跑一次费用流,加上带费用的边再跑一次流量为 k k k的费用流即可
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f,maxn=20005,maxm=400005;
struct edge{
int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k;
inline void add_edge(int u,int v,int cost,int flow){
e[++cnt].v=v;
e[cnt].cost=cost;
e[cnt].flow=flow;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].cost=-cost;
e[cnt].flow=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){
queue<int>q;
memset(dis,0x3f,sizeof dis);
memset(flow,0x3f,sizeof flow);
memset(inque,0,sizeof inque);
q.push(s);
inque[s]=1;
dis[s]=0;
pre[t]=-1;
while(!q.empty()){
int u=q.front();
q.pop();
inque[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].cost&&e[i].flow){
dis[v]=dis[u]+e[i].cost;
pre[v]=u;
last[v]=i;
flow[v]=min(flow[u],e[i].flow);
if(!inque[v]){
q.push(v);
inque[v]=1;
}
}
}
}
if(pre[t]!=-1)return true;
return false;
}
inline pair<int,int> mcmf(int s,int t){
int maxflow=0;
int mincost=0;
while(spfa(s,t)){
int u=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while(u!=s){
e[last[u]].flow-=flow[t];
e[last[u]^1].flow+=flow[t];
u=pre[u];
}
}
return {maxflow,mincost};
}
int u[maxn],v[maxn],w[maxn];
int main(){
cin>>n>>m>>k;
for(int i=1;i<=m;i++){
int c;
scanf("%d%d%d%d",&u[i],&v[i],&c,&w[i]);
add_edge(u[i],v[i],0,c);
}
pair<int,int>ans=mcmf(1,n);
cout<<ans.first<<" ";
add_edge(n,n+1,0,k);
for(int i=1;i<=m;i++){
add_edge(u[i],v[i],w[i],inf);
}
pair<int,int>yaoyao=mcmf(1,n+1);
cout<<yaoyao.second;
return 0;
}
8.P2770 航空路线问题
这题需要转化思维,将一去一回转化成两条不相交的去路
我本想用交换棋子那题一拆三一进一出这样的写,想直接一步到位,结果无限 R E RE RE…
将所有的边全部转化为 西边->东边的单向边,最西和最东的点流量限制为2,其他点全部为1,拆点的费用全为-1
跑完费用流跑两次dfs,一次先序输出,一次后序输出
特判1->n->1,直接输出
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f,maxn=40005,maxm=400005;
struct edge{
int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k,T;
inline void add_edge(int u,int v,int cost,int flow){
e[++cnt].v=v;
e[cnt].cost=cost;
e[cnt].flow=flow;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].cost=-cost;
e[cnt].flow=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){
queue<int>q;
memset(dis,0x3f,sizeof dis);
memset(flow,0x3f,sizeof flow);
memset(inque,0,sizeof inque);
q.push(s);
inque[s]=1;
dis[s]=0;
pre[t]=-1;
while(!q.empty()){
int u=q.front();
q.pop();
inque[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].cost&&e[i].flow){
dis[v]=dis[u]+e[i].cost;
pre[v]=u;
last[v]=i;
flow[v]=min(flow[u],e[i].flow);
if(!inque[v]){
q.push(v);
inque[v]=1;
}
}
}
}
if(pre[t]!=-1)return true;
return false;
}
string str[105];
unordered_map<string,int>mp;
inline pair<int,int> mcmf(int s,int t){
int maxflow=0;
int mincost=0;
int tim=0;
while(spfa(s,t)){
int u=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
++tim;
while(u!=s){
e[last[u]].flow-=flow[t];
e[last[u]^1].flow+=flow[t];
u=pre[u];
}
}
return {maxflow,mincost};
}
int vis[1005];
vector<string>jl1,jl2;
void dfs1(int u){
jl1.push_back(str[u]);
// cout<<str[u]<<endl;
vis[u]=1;
for(int i=head[u+n];i;i=e[i].nex){
int v=e[i].v;
if(e[i].flow!=0||vis[v]==1||v>n||v<1)continue;
else{
dfs1(v);
break;
}
}
}
void dfs2(int u){
// vis[u]=1;
jl2.push_back(str[u]);
for(int i=head[u+n];i;i=e[i].nex){
int v=e[i].v;
if(e[i].flow!=0||vis[v]==1||v>n||v<1)continue;
dfs2(v);
}
// cout<<str[u]<<endl;
}
int main(){
cin>>n>>m;
int num=0;
int s=1;
int t=n*2;
bool ok=false;
for(int i=1;i<=n;i++){
string a;
cin>>a;
mp[a]=i;
str[i]=a;
if(i==1||i==n){
add_edge(i,i+n,-1,2);
}
else{
add_edge(i,i+n,-1,1);
}
}
for(int i=1;i<=m;i++){
string a,b;
cin>>a>>b;
int x=mp[a];
int y=mp[b];
if(x>y)swap(x,y);
if(x==1&&y==n)ok=true;
add_edge(x+n,y,0,1);
}
// add_edge(s,s+n,0,2);
// add_edge(n+n,t,0,2);
pii yaoyao=mcmf(s,t);
if(yaoyao.first==2){
printf("%d\n",-yaoyao.second-2);
dfs1(s);
dfs2(s);
for(auto v:jl1){
cout<<v<<endl;
}
string zz[105];
int numzz=0;
for(auto v:jl2){
zz[++numzz]=v;
}
for(int i=numzz;i>=1;i--){
cout<<zz[i]<<endl;
}
}
else if(yaoyao.first==1&&ok){
cout<<2<<endl;
cout<<str[1]<<endl;
cout<<str[n]<<endl;
cout<<str[1]<<endl;
}
else{
printf("No Solution!");
}
return 0;
}
9.P3159 [CQOI2012]交换棋子
刚看想的太简单,直接莽了一发,意料之中的只过了样例
首先题意要读清楚,相邻!!!相邻的八个点都可以换
然后这一点就有点骚了,因为每个格子有换的次数限制,然后起始点只会换一次,但是路上的点都会被换两次,我们必须要处理这个区别,咋办呢?一拆二好像并不能解决这个问题,所以
一拆三!
每个点拆为原点,入点和出点,入点向原点连边,原点向出点连边
t i m tim tim为这个格子可被交换的次数
1. 1. 1.如果一个点为起始点,最终不为终点:
入点->原点,流量为 t i m / 2 tim/2 tim/2,原点->出点 ( t i m + 1 ) / 2 (tim+1)/2 (tim+1)/2
2. 2. 2.如果一个点为终点,最开始不为起始点:
入点->原点,流量为 ( t i m + 1 ) / 2 (tim+1)/2 (tim+1)/2,原点->出点 t i m / 2 tim/2 tim/2
3. 3. 3.如果一个点既为起点又为终点:
入点->原点,流量为 t i m / 2 tim/2 tim/2,原点->出点 t i m / 2 tim/2 tim/2
统计移动次数,那自然是格子之间的连边费用为 1 1 1,其他全为 0 0 0,边间流量全为 i n f inf inf
考虑一个点被交换的次数为奇数的情况,比如 5 5 5,如果作为出点,考虑第一类点,必须作为出发点,所以至少有点要从它这里跑出去一次,然后就可以作为中间点了,所以它的出发流量为 3 3 3,以此类推,第二类点至少被跑进来一次,然后当中间点, f i n e fine fine
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f,maxn=20005,maxm=400005;
struct edge{
int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k;
const int dx[]={1,1,1,-1,-1,-1,0,0};
const int dy[]={1,-1,0,1,-1,0,1,-1};
inline void add_edge(int u,int v,int cost,int flow){
e[++cnt].v=v;
e[cnt].cost=cost;
e[cnt].flow=flow;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].cost=-cost;
e[cnt].flow=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){
queue<int>q;
memset(dis,0x3f,sizeof dis);
memset(flow,0x3f,sizeof flow);
memset(inque,0,sizeof inque);
q.push(s);
inque[s]=1;
dis[s]=0;
pre[t]=-1;
while(!q.empty()){
int u=q.front();
q.pop();
inque[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].cost&&e[i].flow){
dis[v]=dis[u]+e[i].cost;
pre[v]=u;
last[v]=i;
flow[v]=min(flow[u],e[i].flow);
if(!inque[v]){
q.push(v);
inque[v]=1;
}
}
}
}
if(pre[t]!=-1)return true;
return false;
}
inline pair<int,int> mcmf(int s,int t){
int maxflow=0;
int mincost=0;
while(spfa(s,t)){
int u=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while(u!=s){
e[last[u]].flow-=flow[t];
e[last[u]^1].flow+=flow[t];
u=pre[u];
}
}
return {maxflow,mincost};
}
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 id3(int x,int y){
return (x-1)*m+y+n*m*2;
}
char mp1[25][25],mp2[25][25],mp3[25][25];
int u[maxn],v[maxn],w[maxn];
int main(){
cin>>n>>m;
int s=n*m*3+1;
int t=n*m*3+2;
int cc=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>mp1[i][j];
if(mp1[i][j]=='1')add_edge(s,id2(i,j),0,1),cc++;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>mp2[i][j];
if(mp2[i][j]=='1')add_edge(id2(i,j),t,0,1);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>mp3[i][j];
int tim=mp3[i][j]-'0';
if(mp1[i][j]==mp2[i][j]){
add_edge(id1(i,j),id2(i,j),0,tim/2);
add_edge(id2(i,j),id3(i,j),0,tim/2);
}
if(mp1[i][j]=='1'&&mp2[i][j]=='0'){
add_edge(id1(i,j),id2(i,j),0,tim/2);
add_edge(id2(i,j),id3(i,j),0,(tim+1)/2);
}
if(mp1[i][j]=='0'&&mp2[i][j]=='1'){
add_edge(id1(i,j),id2(i,j),0,(tim+1)/2);
add_edge(id2(i,j),id3(i,j),0,tim/2);
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=0;k<8;k++){
int xx=dx[k]+i;
int yy=dy[k]+j;
if(xx>=1&&xx<=n&&yy<=m&&yy>=1){
add_edge(id3(i,j),id1(xx,yy),1,inf);
}
}
}
}
pair<int,int>yaoyao=mcmf(s,t);
int ans=yaoyao.first;
// cout<<ans<<" ";
if(ans==cc)cout<<yaoyao.second;
else cout<<"-1";
return 0;
}
10.P3356 火星探险问题
建图很裸,拆点连边即可
重点在于输出路径,这里需要用到边的流量的性质,正向边走过的流量就在反向边上加上了,我们可以由此来判断每个点被多少条路线走过, d f s dfs dfs记录走过每个点的次数,每次与反向边的流量比较即可
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f,maxn=20005,maxm=400005;
struct edge{
int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k,T;
inline void add_edge(int u,int v,int cost,int flow){
e[++cnt].v=v;
e[cnt].cost=cost;
e[cnt].flow=flow;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].cost=-cost;
e[cnt].flow=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){
queue<int>q;
memset(dis,0x3f,sizeof dis);
memset(flow,0x3f,sizeof flow);
memset(inque,0,sizeof inque);
q.push(s);
inque[s]=1;
dis[s]=0;
pre[t]=-1;
while(!q.empty()){
int u=q.front();
q.pop();
inque[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].cost&&e[i].flow){
dis[v]=dis[u]+e[i].cost;
pre[v]=u;
last[v]=i;
flow[v]=min(flow[u],e[i].flow);
if(!inque[v]){
q.push(v);
inque[v]=1;
}
}
}
}
if(pre[t]!=-1)return true;
return false;
}
inline pair<int,int> mcmf(int s,int t){
int maxflow=0;
int mincost=0;
while(spfa(s,t)){
int u=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while(u!=s){
e[last[u]].flow-=flow[t];
e[last[u]^1].flow+=flow[t];
u=pre[u];
}
}
return {maxflow,mincost};
}
inline int id(int x,int y){
return (x-1)*m+y;
}
inline int id2(int x,int y){
return (x-1)*m+y+n*m;
}
int vis[maxn];
void dfs(int u,int num){
for(int i=head[u+n*m];i;i=e[i].nex){
int v=e[i].v;
if(v==u+1&&vis[i]<e[i^1].flow){
printf("%d 1\n",num);
vis[i]++;
dfs(v,num);
break;
}
if(v==u+m&&vis[i]<e[i^1].flow){
printf("%d 0\n",num);
vis[i]++;
dfs(v,num);
break;
}
}
}
int mp[105][105];
int main(){
cin>>k>>m>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>mp[i][j];
if(mp[i][j]==1)continue;
add_edge(id(i,j),id2(i,j),mp[i][j]==2?-1:0,1);
add_edge(id(i,j),id2(i,j),0,inf);
}
}
int s=n*m*2+1;
int t=s+1;
if(mp[1][1]!=1)add_edge(s,id(1,1),0,k);
if(mp[n][m]!=1)add_edge(id2(n,m),t,0,k);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(mp[i][j]==1)continue;
if(mp[i][j+1]!=1&&j+1<=m){
add_edge(id2(i,j),id(i,j+1),0,inf);
}
if(mp[i+1][j]!=1&&i+1<=n){
add_edge(id2(i,j),id(i+1,j),0,inf);
}
}
}
pii yaoyao=mcmf(s,t);
for(int i=1;i<=k;i++){
dfs(1,i);
}
return 0;
}
11.P3358 最长k可重区间集问题
先咕一下
12.P4013 数字梯形问题
憨憨题,大概是考验对拆点的理解?
t
a
s
k
1
:
task1:
task1:不允许有路径相交:
很简单,一看就是点和边流量限制都为1,这样就不可能有右边的流跨到左边去了
t
a
s
k
2
:
task2:
task2:允许选重复的点:
点的流量限制无限即可,主要点跟汇点的流量也要改掉
t
a
s
k
3
:
task3:
task3:允许路径相交,允许重复点:
点边都无限即可
建图三次
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f,maxn=40005,maxm=400005;
struct edge{
int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k,T;
inline void add_edge(int u,int v,int cost,int flow){
e[++cnt].v=v;
e[cnt].cost=cost;
e[cnt].flow=flow;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].cost=-cost;
e[cnt].flow=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){
queue<int>q;
memset(dis,0x3f,sizeof dis);
memset(flow,0x3f,sizeof flow);
memset(inque,0,sizeof inque);
q.push(s);
inque[s]=1;
dis[s]=0;
pre[t]=-1;
while(!q.empty()){
int u=q.front();
q.pop();
inque[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].cost&&e[i].flow){
dis[v]=dis[u]+e[i].cost;
pre[v]=u;
last[v]=i;
flow[v]=min(flow[u],e[i].flow);
if(!inque[v]){
q.push(v);
inque[v]=1;
}
}
}
}
if(pre[t]!=-1)return true;
return false;
}
inline pair<int,int> mcmf(int s,int t){
int maxflow=0;
int mincost=0;
while(spfa(s,t)){
int u=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while(u!=s){
e[last[u]].flow-=flow[t];
e[last[u]^1].flow+=flow[t];
u=pre[u];
}
}
return {maxflow,mincost};
}
inline int id(int x,int y){
return (x-1)*(m+n-1)+y;
}
int mp[1000][1000];
int main(){
cin>>m>>n;
int num=10000;
int k=m-1;
int s=20001;
int t=20002;
for(int i=1;i<=n;i++){
k++;
for(int j=1;j<=k;j++){
cin>>mp[i][j];
add_edge(id(i,j),id(i,j)+num,-mp[i][j],1);
if(i==1){
add_edge(s,id(i,j),0,1);
}
if(i==n){
add_edge(id(i,j)+num,t,0,1);
}
}
}
k=m-1;
for(int i=1;i<=n-1;i++){
k++;
for(int j=1;j<=k;j++){
add_edge(id(i,j)+num,id(i+1,j),0,1);
add_edge(id(i,j)+num,id(i+1,j+1),0,1);
}
}
pii yaoyao1=mcmf(s,t);
cout<<-yaoyao1.second<<endl;
memset(head,0,sizeof head);
cnt=1;
for(int i=1;i<=n;i++){
k++;
for(int j=1;j<=k;j++){
// cin>>mp[i][j];
add_edge(id(i,j),id(i,j)+num,-mp[i][j],inf);
if(i==1){
add_edge(s,id(i,j),0,1);
}
if(i==n){
add_edge(id(i,j)+num,t,0,inf);
}
}
}
k=m-1;
for(int i=1;i<=n-1;i++){
k++;
for(int j=1;j<=k;j++){
add_edge(id(i,j)+num,id(i+1,j),0,1);
add_edge(id(i,j)+num,id(i+1,j+1),0,1);
}
}
pii yaoyao2=mcmf(s,t);
cout<<-yaoyao2.second<<endl;
memset(head,0,sizeof head);
cnt=1;
for(int i=1;i<=n;i++){
k++;
for(int j=1;j<=k;j++){
// cin>>mp[i][j];
add_edge(id(i,j),id(i,j)+num,-mp[i][j],inf);
if(i==1){
add_edge(s,id(i,j),0,1);
}
if(i==n){
add_edge(id(i,j)+num,t,0,inf);
}
}
}
k=m-1;
for(int i=1;i<=n-1;i++){
k++;
for(int j=1;j<=k;j++){
add_edge(id(i,j)+num,id(i+1,j),0,inf);
add_edge(id(i,j)+num,id(i+1,j+1),0,inf);
}
}
pii yaoyao3=mcmf(s,t);
cout<<-yaoyao3.second<<endl;
return 0;
}
13.P4015 运输问题
?
迷惑题目
裸跑一次最大和一次最小费用流即可
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int inf=0x3f3f3f3f,maxn=40005,maxm=400005;
struct edge{
int nex,v,cost,flow;
}e[maxm];
int head[maxn],cnt=1;
int n,m,k,T;
inline void add_edge(int u,int v,int cost,int flow){
e[++cnt].v=v;
e[cnt].cost=cost;
e[cnt].flow=flow;
e[cnt].nex=head[u];
head[u]=cnt;
e[++cnt].v=u;
e[cnt].cost=-cost;
e[cnt].flow=0;
e[cnt].nex=head[v];
head[v]=cnt;
}
int inque[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn];
bool spfa(int s,int t){
queue<int>q;
memset(dis,0x3f,sizeof dis);
memset(flow,0x3f,sizeof flow);
memset(inque,0,sizeof inque);
q.push(s);
inque[s]=1;
dis[s]=0;
pre[t]=-1;
while(!q.empty()){
int u=q.front();
q.pop();
inque[u]=0;
for(int i=head[u];i;i=e[i].nex){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].cost&&e[i].flow){
dis[v]=dis[u]+e[i].cost;
pre[v]=u;
last[v]=i;
flow[v]=min(flow[u],e[i].flow);
if(!inque[v]){
q.push(v);
inque[v]=1;
}
}
}
}
if(pre[t]!=-1)return true;
return false;
}
unordered_map<string,int>mp;
inline pair<int,int> mcmf(int s,int t){
int maxflow=0;
int mincost=0;
while(spfa(s,t)){
int u=t;
maxflow+=flow[t];
mincost+=flow[t]*dis[t];
while(u!=s){
e[last[u]].flow-=flow[t];
e[last[u]^1].flow+=flow[t];
u=pre[u];
}
}
return {maxflow,mincost};
}
inline int id(int i,int j){
return (i-1)*n+j;
}
int a[50005],b[50005],x[50005];
int main(){
cin>>m>>n;
int s=n+m+1;
int t=n+m+2;
for(int i=1;i<=m;i++){
scanf("%d",&a[i]);
add_edge(s,i,0,a[i]);
}
for(int i=1;i<=n;i++){
scanf("%d",&b[i]);
add_edge(i+m,t,0,b[i]);
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
scanf("%d",&x[id(i,j)]);
add_edge(i,j+m,x[id(i,j)],a[i]);
}
}
pii yaoyao=mcmf(s,t);
cout<<yaoyao.second<<endl;
cnt=1;
memset(head,0,sizeof head);
for(int i=1;i<=m;i++){
add_edge(s,i,0,a[i]);
}
for(int i=1;i<=n;i++){
add_edge(i+m,t,0,b[i]);
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
add_edge(i,j+m,-x[id(i,j)],a[i]);
}
}
pii yaoyao2=mcmf(s,t);
cout<<-yaoyao2.second<<endl;
return 0;
}
14.P5331 [SNOI2019]通信
咕 咕 咕