T1 飞行员配对方案
二分图最大匹配。这里写个匈牙利算法
#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
struct edge{
int x,y,next;
edge(){}
edge(int _x,int _y,int _nt):x(_x),y(_y),next(_nt){}
}e[20005];
int head[maxn],tot=0;
#define addedge(x,y) {
e[++tot]=edge(x,y,head[x]);head[x]=tot;
}
int n,m;
bool vst[maxn];
int match[maxn];
int DFS(int x){
for(int i=head[x];i;i=e[i].next){
int y=e[i].y;
if(vst[y])continue;vst[y]=1;
if(!match[y] || DFS(match[y])){
match[y]=x;
return 1;
}
}
return 0;
}
int main(){
scanf("%d%d",&n,&m);
int x,y;
while(cin>>x>>y && x+y>0)addedge(x,y);
int ans=0;
for(int i=1;i<=n;i++){
memset(vst,0,sizeof(vst));
ans+=DFS(i);
}
printf("%d
",ans);
}
T2 太空飞行计划
最大权闭合子图
S向实验i连一条流量为Ci的边。仪器i向T连一条流量为Ci的边,每组实验i与仪器j的关系连一条流量为INF的边
答案为全部实验的收入的和减去网络最大流(最小割)
细致考虑割掉了一些什么样的边
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<queue>
using namespace std;
const int maxn=110;
const int maxm=10010;
const int INF=0x3f3f3f3f;
struct edge{
int x,y,next,v;
edge(){}
edge(int _x,int a,int b,int c):x(_x),y(a),next(b),v(c){}
} e[maxm<<1];
int head[maxn],tot=1,h[maxn],cur[maxn];
int n,m,S,T;
void addedge(int x,int y,int v){
e[++tot]=edge(x,y,head[x],v);head[x]=tot;
e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
bool BFS(){
queue<int>q;
for(int i=1;i<=T;i++)h[i]=-1;
h[S]=0;q.push(S);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].next){
int y=e[i].y;int v=e[i].v;
if(h[y]==-1 && v){
h[y]=h[x]+1;
q.push(y);
}
}
}
return h[T]!=-1;
}
int DFS(int x,int f){
int tmp,used=0;
if(x==T)return f;
for(int i=cur[x];i;i=e[i].next){
int y=e[i].y,v=e[i].v;
if(h[y]==h[x]+1 && v){
tmp=DFS(y,min(v,f-used));
e[i].v-=tmp;if(e[i].v)cur[x]=i;
e[i^1].v+=tmp;
used+=tmp;
if(used==f)return used;
}
}
if(!used)h[x]=-1;
return used;
}
int maxf(){
int ret=0;
while(BFS()){
for(int i=1;i<=T;i++)cur[i]=head[i];
ret+=DFS(S,INF);
}
return ret;
}
int main(){
scanf("%d%d",&n,&m);
S=n+m+1,T=S+1;
int ans=0;
for(int i=1;i<=n;i++){
int c;
scanf("%d",&c);
addedge(S,i,c);
ans+=c;
int d;
while(scanf("%d",&d)&&(d!=0)){
addedge(i,d+n,INF);
}
}
for(int i=1;i<=m;i++){
int c;
scanf("%d",&c);
addedge(i+n,T,c);
}
ans-=maxf();
for(int i=1;i<=n;i++)if(h[i]!=-1)cout<<i<<" ";cout<<endl;
for(int i=1;i<=m;i++)
if(h[i+n]!=-1)cout<<i<<" ";cout<<endl;
cout<<ans<<endl;
}
T3 最小路径覆盖
求有向无环图最小路径覆盖
拆点构造二分图,原图中每一个点i拆成二分图X,Y集合中的两个点Xi,Yi。对于原图中的每条边(i,j),在二分图中连边(Xi,Yi)。最小路径覆盖条数等于原图点数减去最大匹配数。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=40010;
const int maxm=80010;
const int INF=0x3f3f3f3f;
struct edge{
int y,next,v;
edge(){}
edge(int a,int b,int c):y(a),next(b),v(c){}
} e[maxm<<1];
int head[maxn],tot=1;
int n,m,S,T;
void addedge(int x,int y,int v){
e[++tot]=edge(y,head[x],v);head[x]=tot;
e[++tot]=edge(x,head[y],0);head[y]=tot;
}
int cur[maxn],h[maxn];
#include<queue>
queue<int>q;
bool BFS(){
memset(h,-1,sizeof(h));
q.push(S);h[S]=0;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].next){
int y=e[i].y,v=e[i].v;
if(h[y]==-1 && v){
q.push(y);
h[y]=h[x]+1;
}
}
}
return h[T]!=-1;
}
int mark[maxn],to[maxn];
int DFS(int x,int f){
if(x==T)return f;
int tmp,used=0;
for(int i=cur[x];i;i=e[i].next){
int y=e[i].y;
int v=e[i].v;
if(v && h[y]==h[x]+1){
tmp=DFS(y,min(v,f-used));
e[i].v-=tmp;
e[i^1].v+=tmp;
used+=tmp;
if(tmp){
to[x]=y;
if(y>n)mark[y-n]=1;
}
if(e[i].v)cur[x]=i;
if(used==f)return f;
}
}
if(!used)h[x]=-1;
return used;
}
int maxf(){
int ret=0;
while(BFS()){
for(int i=1;i<=T;i++)cur[i]=head[i];
ret+=DFS(S,INF);
}
return ret;
}
int main(){
scanf("%d%d",&n,&m);
S=n*2+1;T=S+1;
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y+n,1);
}
for(int i=1;i<=n;i++){
addedge(S,i,1);
addedge(i+n,T,1);
}
memset(to,0,sizeof(to));
memset(mark,0,sizeof(mark));
printf("%d
",n-maxf());
for(int i=1;i<=n;i++){
if(mark[i])continue;
printf("%d",i);
int y=i;
while(to[y]){
printf(" %d",to[y]-n);
y=to[y]-n;
}
printf(" 0
");
}
}
T4 魔术球问题
枚举答案+最小路径覆盖
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
using namespace std;
const int maxn=4010;
const int maxm=200010;
const int INF=0x3f3f3f3f;
const int offset=2000;
struct edge{
int x,y,next,v;
edge(){}
edge(int _x,int a,int b,int c):x(_x),y(a),next(b),v(c){}
} e[maxm<<1];
int head[maxn],tot=1;
void addedge(int x,int y,int v){
e[++tot]=edge(x,y,head[x],v);head[x]=tot;
e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
bool issquare[maxn];
int S,T,n;
#include<queue>
queue<int>q;
int h[maxn];
bool BFS(){
memset(h,-1,sizeof(h));
h[S]=0;q.push(S);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].next){
int y=e[i].y;
int v=e[i].v;
if(v && h[y]==-1){
h[y]=h[x]+1;
q.push(y);
}
}
}
return h[T]!=-1;
}
int DFS(int x,int f){
if(x==T)return f;
int used=0,tmp;
for(int i=head[x];i;i=e[i].next){
int y=e[i].y;
int v=e[i].v;
if(v && h[y]==h[x]+1){
tmp=DFS(y,min(v,f-used));
e[i].v-=tmp;
e[i^1].v+=tmp;
used+=tmp;
if(used==f)return f;
}
}
if(!used)h[x]=-1;
return used;
}
int ret=0;
int maxf(){
while(BFS())ret+=DFS(S,INF);
return ret;
}
int main(){
scanf("%d",&n);
memset(issquare,0,sizeof(issquare));
for(int i=1;i<=60;i++)issquare[i*i]=1;
S=0;T=maxn-1;
addedge(S,1,1);
addedge(1+offset,T,1);
int tmp,A;
for(A=1;A-maxf() <= n;){
++A;
addedge(S,A,1);
addedge(A+offset,T,1);
for(int j=1;j<A;j++){
if(issquare[A+j])
addedge(j,A+offset,1);
}
}
int ANS=A-1;
printf("%d
",ANS);
}
T5 圆桌问题
二分图多重匹配
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<cstdlib>
using namespace std;
const int maxn=2010;
const int maxm=400010;
const int INF=0x3f3f3f3f;
struct edge{
int x,y,next,v;
edge(){}
edge(int _x,int _y,int _nt,int _v):
x(_x),y(_y),next(_nt),v(_v){}
} e[maxm];
int head[maxn],tot=1;
void addedge(int x,int y,int v){
e[++tot]=edge(x,y,head[x],v);head[x]=tot;
e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
int cur[maxn],h[maxn];
int n,m;
int a[maxn],b[maxn];
int S,T;
queue<int>q;
bool BFS(){
memset(h,-1,sizeof(h));
q.push(S);h[S]=0;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].next){
int y=e[i].y;
int v=e[i].v;
if( v && h[y]==-1){
h[y]=h[x]+1;
q.push(y);
}
}
}
return h[T]!=-1;
}
int DFS(int x,int f){
if(x==T)return f;
int used=0,tmp;
for(int i=cur[x];i;i=e[i].next){
int y=e[i].y;
int v=e[i].v;
if(v && h[y]==h[x]+1){
tmp=DFS(y,min(v,f-used));
e[i].v-=tmp;
e[i^1].v+=tmp;
used+=tmp;
if(e[i].v)cur[x]=i;
if(used==f)return f;
}
}
if(!used)h[x]=-1;
return used;
}
int maxf(){
int ret=0;
while(BFS()){
for(int i=S;i<=T;i++)cur[i]=head[i];
ret+=DFS(S,INF);
}
return ret;
}
int main(){
scanf("%d%d",&n,&m);
S=0;T=n+m+1;
int sum=0;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=m;i++)scanf("%d",&b[i]);
for(int i=1;i<=n;i++)sum+=a[i];
for(int i=1;i<=n;i++)
addedge(S,i,a[i]);
for(int i=1;i<=m;i++)
addedge(i+n,T,b[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
addedge(i,n+j,1);
int tmp=maxf();
if(tmp == sum)puts("1");else puts("0");
}
T6 最长递增子序列
DP+最大流
#include<cstdio>
#include<cstring>
#include<iostream>
#include<queue>
#include<cstdlib>
using namespace std;
const int maxn=2010;
const int maxm=400010;
const int INF=0x3f3f3f3f;
struct edge{
int x,y,next,v;
edge(){}
edge(int a,int b,int c,int d):x(a),y(b),next(c),v(d){}
}e[maxm];
int head[maxn],tot=1;
int S,T;
void addedge(int x,int y,int v){
e[++tot]=edge(x,y,head[x],v);head[x]=tot;
e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
int h[maxn],cur[maxn];
int a[maxn],f[maxn],n;
queue<int>q;
bool BFS(){
memset(h,-1,sizeof(h));
h[S]=0;q.push(S);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].next){
int y=e[i].y;
int v=e[i].v;
if(h[y]==-1 && v){
h[y]=h[x]+1;
q.push(y);
}
}
}
return h[T]!=-1;
}
int DFS(int x,int f){
if(x==T)return f;
int used=0,tmp;
for(int i=cur[x];i;i=e[i].next){
int y=e[i].y;
int v=e[i].v;
if(h[y]==h[x]+1 && v){
tmp=DFS(y,min(v,f-used));
e[i].v-=tmp;
e[i^1].v+=tmp;
used+=tmp;
if(e[i].v)cur[x]=i;
if(used==f)return f;
}
}
if(!used)h[x]=-1;
return used;
}
int ret=0;
int maxf(){
while(BFS()){
for(int i=S;i<=T;i++)cur[i]=head[i];
ret+=DFS(S,INF);
}
return ret;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
f[i]=1;
}
for(int i=n-1;i>=1;i--){
for(int j=i+1;j<=n;j++)
if(a[i]<a[j])f[i]=max(f[i],f[j]+1);
}
int ans1=0;
for(int i=1;i<=n;i++)ans1=max(ans1,f[i]);
printf("%d
",ans1);
memset(head,0,sizeof(head));
tot=1;
S=0;T=n*2+8;
for(int i=1;i<=n;i++){
addedge(i,i+n,1);
if(f[i]==ans1)addedge(S,i,1);
if(f[i]==1) addedge(i+n,T,1);
for(int j=i+1;j<=n;j++)
if(f[i]==f[j]+1 && a[i]<a[j])
addedge(i+n,j,1);
}
printf("%d
",maxf());
addedge(1,1+n,INF);
addedge(n,n+n,INF);
if(f[1]==ans1)addedge(S,1,INF);
addedge(n+n,T,INF);
printf("%d
",maxf());
}
T7 试题库问题
二分图多重匹配
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<queue>
using namespace std;
const int maxn=2010;
const int maxm=400010;
const int INF=0x3f3f3f3f;
struct edge{
int x,y,next,v;
edge(){}
edge(int a,int b,int c,int d)
:x(a),y(b),next(c),v(d){}
} e[maxm];
int head[maxn],tot=1;
void addedge(int x,int y,int v){
e[++tot]=edge(x,y,head[x],v);head[x]=tot;
e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
int S,T;
int n,m,k;
int cur[maxn],h[maxn];
queue<int>q;
bool BFS(){
memset(h,-1,sizeof(h));
h[S]=0;q.push(S);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].next){
int y=e[i].y;
int v=e[i].v;
if(v && h[y]==-1){
h[y]=h[x]+1;
q.push(y);
}
}
}
return h[T]!=-1;
}
int DFS(int x,int f){
if(x==T)return f;
int used=0,tmp;
for(int i=cur[x];i;i=e[i].next){
int y=e[i].y;
int v=e[i].v;
if(v && h[y]==h[x]+1){
tmp=DFS(y,min(v,f-used));
e[i].v-=tmp;
e[i^1].v+=tmp;
used+=tmp;
if(e[i].v)cur[x]=i;
if(used==f)return f;
}
}
if(!used)h[x]=-1;
return used;
}
int ret=0;
int maxf(){
while(BFS()){
for(int i=S;i<=T;i++)cur[i]=head[i];
ret+=DFS(S,INF);
}
return ret;
}
int main(){
scanf("%d%d",&k,&n);
S=0;T=n+k+1;m=0;
for(int i=1;i<=k;i++){
int v;
scanf("%d",&v);
m+=v;
addedge(n+i,T,v);
}
for(int i=1;i<=n;i++){
int p,y;
addedge(S,i,INF);
scanf("%d",&p);
for(int j=1;j<=p;j++){
scanf("%d",&y);
addedge(i,y+n,1);
}
}
if(maxf()>=m)puts("YES");
else puts("No Solution!");
}
T9 方格取数问题
二分图最大点权独立集
对格子黑白染色。相邻格子连容量正无穷的边。S向白格连流量为格中数值的边,黑格向T连流量为格中数值的边。求最小割,答案为全部格子数值之和减去最小格
最大点权独立集=点权和-最小点权覆盖集=点权和-最小割集
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<queue>
using namespace std;
const int maxn=510;
const int maxm=1000010;
const int INF=0x3f3f3f3f;
struct edge{
int x,y,next,v;
edge(){}
edge(int a,int b,int c,int d)
:x(a),y(b),next(c),v(d){}
} e[maxm];
int head[maxn],tot=1;
void addedge(int x,int y,int v){
e[++tot]=edge(x,y,head[x],v);head[x]=tot;
e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
int S,T,n,m;
int cur[maxn],h[maxn];
int ret=0;
queue<int>q;
bool BFS(){
memset(h,-1,sizeof(h));
h[S]=0;q.push(S);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=e[i].next){
int y=e[i].y;
int v=e[i].v;
if(h[y]==-1 && v){
h[y]=h[x]+1;
q.push(y);
}
}
}
return h[T]!=-1;
}
int DFS(int x,int f){
if(x==T)return f;
int used=0,tmp;
for(int i=cur[x];i;i=e[i].next){
int y=e[i].y;
int v=e[i].v;
if(h[y]==h[x]+1 && v){
tmp=DFS(y,min(v,f-used));
e[i].v-=tmp;
e[i^1].v+=tmp;
used+=tmp;
if(e[i].v)cur[x]=i;
if(used==f)return f;
}
}
if(!used)h[x]=-1;
return used;
}
int maxf(){
while(BFS()){
for(int i=S;i<=T;i++)cur[i]=head[i];
ret+=DFS(S,INF);
}
return ret;
}
const int mx[]={0,1,-1,0};
const int my[]={1,0,0,-1};
int num(int x,int y){
return (x-1)*m+y;
}
int main(){
scanf("%d%d",&n,&m);
S=0;T=n*m+1;
int sum=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
int v;
scanf("%d",&v);
sum+=v;
int id=num(i,j);
if((i+j)%2==0){
addedge(S,id,v);
for(int k=0;k<4;k++){
int nx=i+mx[k];
int ny=j+my[k];
if(nx >=1 && nx<=n && ny>=1 && ny<=m){
addedge(id,num(nx,ny),INF);
}
}
}
else
addedge(id,T,v);
}
printf("%d
",sum-maxf());
}
T10 餐巾问题
供求平衡问题。费用流解决
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 2010;
const int maxm = 4000010;
const int INF = 0x3f3f3f3f;
struct edge{
int x,y,next,v,c;
edge(){}
edge(int _x,int _y,int _nt,int _v,int _c)
:x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[maxm];
int head[maxn],tot=1,dis[maxn],fro[maxn],a[maxn];
bool inq[maxn];
int S,T,n,m,p,f,tt,ss;
void addedge(int x,int y,int v,int c){
e[++tot]=edge(x,y,head[x],v,c);head[x]=tot;
e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
}
queue<int>q;
bool SPFA(){
for(int i=S;i<=T;i++){
dis[i]=INF;
inq[i]=0;
}
q.push(S);inq[S]=1;
dis[S]=0;
while(!q.empty()){
int x=q.front();q.pop();
inq[x]=0;
// cout<<x<<":"<<endl;;
for(int i=head[x];i;i=e[i].next){
int y=e[i].y;
// cout<<y<<endl;
int v=e[i].v;
int c=e[i].c;
if(v && dis[x]+c<dis[y]){
dis[y]=dis[x]+c;
fro[y]=i;
if(!inq[y]){
inq[y]=1;
q.push(y);
}
}
}
}
return dis[T]!=INF;
}
int mcf(){
int ret=0;
while(SPFA()){
int tmp=INF;
for(int i=fro[T];i;i=fro[e[i].x]){
tmp=min(tmp,e[i].v);
}
ret+=tmp*dis[T];
for(int i=fro[T];i;i=fro[e[i].x]){
e[i].v-=tmp;
e[i^1].v+=tmp;
}
}
return ret;
}
int main(){
scanf("%d%d%d%d%d%d",&n,&p,&m,&f,&tt,&ss);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
S=0;T=n*2+4;
for(int i=1;i<=n;i++){
addedge(S,i,a[i],0);
addedge(i+n,T,a[i],0);
addedge(S,i+n,INF,p);
if(i+1<=n)addedge(i,i+1,INF,0);
if(i+m<=n)addedge(i,i+m+n,INF,f);
if(i+tt<=n)addedge(i,i+tt+n,INF,ss);
}
printf("%d
",mcf());
}
T11 航空路线问题
最大费用最大流求两条不相交路径
拆点,每一个城市i拆为Ai和Bi两个点,Ai到Bi连流量为1费用为1的边。A1到B1,An到Bn连流量为2费用为1的边。
对于给出的边(i,j)。连Bi-Aj流量为1,费用为0。
求最大费用最大流mcf。若A1-B1、An-Bn满流,则有解为mcf-2,否则无解。
#include<bits/stdc++.h>
using namespace std;
#define MADOKA main
int hash(char *str){
int ret=0,seed=131;
for(;*str;ret=ret*seed+*str++);
return ret & 0x7fffffff;
}
map<int,int>mp;
struct edge{
int x,y,next,v,c;
edge(){}
edge(int _x,int _y,int _nt,int _v,int _c):x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[20005];
int head[105],tot=1,dis[105],fro[105],a[105];
bool inq[105];
queue<int>q;
char str[1000];
#define addedge(x,y,v,c) {
e[++tot]=edge(x,y,head[x],v,c);head[x]=tot;
e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
}
int S,T,n,m;
bool SPFA(){
for(int i=S;i<=T;i++)dis[i]=inq[i]=0;
q.push(S);inq[S]=1;
dis[S]=0;
while(!q.empty()){
int x=q.front();q.pop();
inq[x]=0;
for(int i=head[x];i;i=e[i].next){
int y=e[i].y;
int v=e[i].v;
int c=e[i].c;
if(v && dis[x]+c>dis[y]){
dis[y]=dis[x]+c;
fro[y]=i;
if(!inq[y]){
inq[y]=1;q.push(y);
}
}
}
}
return dis[T]!=0;
}
int mcf(){
int ret=0;
while(SPFA()){
int tmp=100;
for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
ret+=tmp*dis[T];
for(int i=fro[T];i;i=fro[e[i].x]){
e[i].v-=tmp;
e[i^1].v+=tmp;
}
}
return ret;
}
int MADOKA(){
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
scanf("%d%d",&n,&m);
S=1;T=n*2;
for(int i=1;i<=n;i++){
scanf("%s",str);
mp[hash(str)]=i;
if(i==1 || i==n){
addedge(i,i+n,2,1);
}else{
addedge(i,i+n,1,1);
}
}
for(int x,y,i=1;i<=m;i++){
scanf("%s",str);x=mp[hash(str)];
scanf("%s",str);y=mp[hash(str)];
if(x==1 && y==n){
addedge(x+n,y,2,0);continue;
}
if(x==n && y==1){
addedge(y+n,x,2,0);continue;
}
if(x < y){
addedge(x+n,y,1,0);
}else{
addedge(y+n,x,1,0);
}
}
int ans=mcf()-2;
if(e[2].v==0 && e[n<<1].v==0)
printf("%d
",ans);
else puts("No Solution!");
}
T13 星际转移问题
分层图网络流
太空船为边,以天数分层,从天数为0開始枚举加边求最大流直到最大流达到K为止
#include<bits/stdc++.h>
using namespace std;
#define MADOKA main
const int maxn = 100005;
const int INF = 0x3f3f3f3f;
int f[maxn];
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
struct edge{
int x,y,next,v;
edge(){}
edge(int _x,int _y,int _nt,int _v)
:x(_x),y(_y),next(_nt),v(_v){}
}e[maxn<<1];
int head[maxn],tot=1,n,m,k,S,T,DAY,cur[maxn],h[maxn];
int maxf=0;
void addedge(int x,int y,int v){
e[++tot]=edge(x,y,head[x],v);head[x]=tot;
e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
queue<int>q;
bool BFS(){
memset(h,-1,sizeof(h));
q.push(S);h[S]=0;
while(!q.empty()){
int x=q.front();q.pop();
for(int y,v,i=head[x];i;i=e[i].next){
y=e[i].y;v=e[i].v;
if(v && h[y]==-1){
h[y]=h[x]+1;
q.push(y);
}
}
}
return h[T]!=-1;
}
int DFS(int x,int f){
if(x==T)return f;
int used=0,tmp;
for(int y,v,i=cur[x];i;i=e[i].next){
y=e[i].y;v=e[i].v;
if(v && h[y]==h[x]+1){
tmp=DFS(y, min(v,f-used));
e[i].v-=tmp;
e[i^1].v+=tmp;
used+=tmp;
if(e[i].v)cur[x]=i;
if(used==f)return f;
}
}
if(!used)h[x]=-1;
return used;
}
void dinic(){
while(BFS()){
for(int i=S;i<=T;i++)cur[i]=head[i];
maxf+=DFS(S,INF);
}
}
struct ship{
int p,r;
int s[25];
}p[maxn];
int getnum(int N,int DAY){
return DAY*(n+2)+N;
}
int MADOKA(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n+2;i++)f[i]=i;
S=0;T=20005;
for(int i=1;i<=m;i++){
scanf("%d%d",&p[i].p,&p[i].r);
int c;
for(int j=1;j<=p[i].r;j++){
scanf("%d",&c);
if(c== 0)c=n+1;
if(c==-1)c=n+2;
p[i].s[j]=c;
if(j>1){
int x=p[i].s[j-1];
int y=p[i].s[j];
x=find(x);y=find(y);
f[x]=y;
}
}
}
if(find(n+1)!=find(n+2)){puts("0");return 0;}
addedge(S,getnum(n+1,0),INF);
addedge(getnum(n+2,0),T,INF);
int DAY;maxf=0;
for(DAY=1;maxf<k;DAY++){
addedge(S,getnum(n+1,DAY),INF);
addedge(getnum(n+2,DAY),T,INF);
for(int i=1;i<=n+2;i++)
addedge(getnum(i,DAY-1),getnum(i,DAY),INF);
for(int i=1;i<=m;i++){
int x=p[i].s[ (DAY-1) % p[i].r +1 ];
int y=p[i].s[ ( DAY ) % p[i].r +1 ];
addedge(getnum(x,DAY-1),getnum(y,DAY),p[i].p);
}
dinic();
}
printf("%d
",DAY-1);
}
T14 孤岛营救
分层图最短路
dis[p,x]记录钥匙获得情况为p,当前到达x号点的最小时间。BFS转移就可以
#include<bits/stdc++.h>
using namespace std;
const int maxn = 20;
const int INF = 0x3f3f3f3f;
int n,m,p,k,s;
const int mx[]={1,0,-1,0};
const int my[]={0,1,0,-1};
int g[255][255];
vector<int> key[20][20];
int dis[3200][255];
typedef pair<int,int>pii;
queue<pii>q;
int encode(int x,int y){
return (x-1)*m+y;
}
void decode(int d,int &x,int &y){
y=d%m;if(y==0)y=m;
x=(d-y)/m+1;
}
int main(){
scanf("%d%d%d",&n,&m,&p);
scanf("%d",&k);
memset(g,0,sizeof(g));
for(int i=1;i<=k;i++){
int x1,y1,x2,y2,G;
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&G);
int p=encode(x1,y1),q=encode(x2,y2);
g[ p ][ q ]=g[ q ][ p ]=G?G:INF;
}
scanf("%d",&s);
memset(key,0,sizeof(key));
for(int i=1;i<=s;i++){
int x,y,Q;
scanf("%d%d%d",&x,&y,&Q);
key[x][y].push_back(Q);
}
memset(dis,-1,sizeof(dis));
for(int i=1;i<=n*m;i++)
for(int j=1;j<=n*m;j++){
if(i==j)continue;
int x1,y1,x2,y2;
if(g[i][j]==0)continue;
decode(i,x1,y1);decode(j,x2,y2);
}
int tmp=1;
for(int i=0;i<key[1][1].size();i++)tmp|=1<<key[1][1][i];
dis[ tmp ][ encode(1,1) ]=0;
q.push(pii(tmp,encode(1,1)));
while(!q.empty()){
pii u=q.front();q.pop();
int x=u.second,p=u.first;
int tx,ty;
decode(x,tx,ty);
if(tx==n && ty==m){
cout<<dis[p][x]<<endl;
return 0;
}
for(int i=0;i<4;i++){
int nx=tx+mx[i];
int ny=ty+my[i];
if(nx<1 || nx>n || ny<1 || ny>m)continue;
int y=encode(nx,ny);
if(g[x][y]==INF)continue;
if(g[x][y]==0 || (p & (1<<g[x][y]))){
int np=p;
for(int j=0;j<key[nx][ny].size();j++)
np|=(1<<key[nx][ny][j]);
if(dis[np][y]==-1){
dis[np][y]=dis[p][x]+1;
q.push(pii(np,y));
}
}
}
}
puts("-1");
}
T15 汽车加油行驶
分层图最短路
#include<bits/stdc++.h>
using namespace std;
const int maxn = 200005;
const int INF= 0x3f3f3f3f;
int N,K,A,B,C;
int g[105][105],cnt=0;
int num(int x,int y,int h){
return h*N*N + (x-1) * N +y;
}
struct edge{
int x,y,next,c;
edge(){}
edge(int _x,int _y,int _nt,int _c):x(_x),y(_y),next(_nt),c(_c){}
}e[maxn<<2];
int head[maxn],tot=1,dis[maxn];
void addedge(int x,int y,int c){
e[++tot]=edge(x,y,head[x],c);head[x]=tot;
}
const int mx[]={1,0,-1,0};
const int my[]={0,1,0,-1};
typedef pair<int,int>pii;
struct heap{
pii data[maxn<<2];
int siz;
public:
heap(){siz=0;}
void push(pii x);
void pop();
pii top(){return data[1];}
bool empty(){return siz==0;}
};
heap q;
void Dijkstra(){
int S=num(1,1,K);
int T=num(N,N,K)+1;
for(int i=1;i<=T;i++)dis[i]=i==S?0:INF;
q.push(pii(dis[S],S));
while(!q.empty()){
pii tmp=q.top();q.pop();
int x=tmp.second;
if(tmp.first > dis[x])continue;
for(int y,c,i=head[x];i;i=e[i].next){
y=e[i].y;c=e[i].c;
if(dis[y] > dis[x]+c){
dis[y]=dis[x]+c;
q.push(pii(dis[y],y));
}
}
}
}
int main(){
// freopen("trav.in","r",stdin);
// freopen("trav2.out","w",stdout);
scanf("%d%d%d%d%d",&N,&K,&A,&B,&C);
for(int i=1;i<=N;i++)for(int j=1;j<=N;j++){
scanf("%d",&g[i][j]);
}
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
for(int p=0;p<=K;p++){
int c=g[i][j]?A:A+C;
if(p<K)addedge(num(i,j,p),num(i,j,K),c);
if( (!g[i][j] && p>0) || p==K){
for(int q=0;q<4;q++){
int x=i+mx[q];
int y=j+my[q];
c=q<2 ? 0 : B;
if(x>
=1 && x<=N && y>=1 && y<=N)
addedge(num(i,j,p),num(x,y,p-1),c);
}
}
}
Dijkstra();
int ans=INF;
for(int i=0;i<=K;i++)ans=min(ans,dis[num(N,N,i)]);
printf("%d
",ans);
}
void heap::push(pii x){
data[++siz]=x;int now=siz;
while(now>>1){
if(data[now].first<data[now>>1].first){
swap(data[now],data[now>>1]);
now>>=1;
}else break;
}
}
void heap::pop(){
data[1]=data[siz--];int now=1,next;
while(now<<1 < siz){
if(data[now<<1].first<data[now<<1|1].first)
next=now<<1;else next=now<<1|1;
if(data[now].first>data[next].first){
swap(data[now],data[next]);now=next;
}else break;
}
}
T16 数字梯形问题
费用流求不相交路径
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int maxn = 50;
const int INF = 0x3f3f3f3f;
int n,m,cnt;
int a[maxn][maxn],num[maxn][maxn];
struct edge{
int x,y,next,v,c;
edge(){}
edge(int _x,int _y,int _nt,int _v,int _c):x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[maxn<<6];
int head[maxn*maxn*2],tot,fro[maxn*maxn*2],S,T;
LL dis[maxn*maxn*2];
bool inq[maxn*maxn*2];
queue<int>q;
inline void addedge(int x,int y,int v,int c){
e[++tot]=edge(x,y,head[x],v,c);head[x]=tot;
e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
// cout<<tot<<endl;
}
bool SPFA(){
for(int i=S;i<=T;i++)dis[i]=-INF;
dis[S]=0;
q.push(S);inq[S]=1;
while(!q.empty()){
int x=q.front();q.pop();inq[x]=0;
for(int y,v,c,i=head[x];i;i=e[i].next){
y=e[i].y;v=e[i].v;c=e[i].c;
if(v && dis[x]+c > dis[y]){
dis[y]=dis[x]+c;
fro[y]=i;
if(!inq[y]){
q.push(y);inq[y]=1;
}
}
}
}
return dis[T]!=-INF;
}
LL mcf(){
LL ret=0;
while(SPFA()){
int tmp=INF;
for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
ret+=tmp*dis[T];
for(int i=fro[T];i;i=fro[e[i].x])e[i].v-=tmp,e[i^1].v+=tmp;
}
return ret;
}
int main(){
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=m+i-1;j++)
scanf("%d",&a[i][j]);
cnt=0;
for(int i=1;i<=n;i++)for(int j=1;j<=m+i-1;j++)num[i][j]=++cnt;
S=0;T=cnt*2+1;
/*PROBLEM 1*/
memset(head,0,sizeof(head));tot=1;memset(fro,0,sizeof(fro));
for(int i=1;i<=m;i++)addedge(S,num[1][i],1,0);
for(int i=1;i<=m+n-1;i++){addedge(num[n][i]+cnt,T,1,0);addedge(num[n][i],num[n][i]+cnt,1,a[n][i]);}
for(int i=1;i<n;i++)for(int j=1;j<=m+i-1;j++){
addedge(num[i][j],num[i][j]+cnt,1,a[i][j]);
addedge(num[i][j]+cnt,num[i+1][j],1,0);
addedge(num[i][j]+cnt,num[i+1][j+1],1,0);
}
// for(int i=2;i<=tot;i+=2)cout<<e[i].x<<" "<<e[i].y<<" "<<e[i].v<<" "<<e[i].c<<endl;
printf("%lld
",mcf());
/*PROBLEM 2*/
memset(head,0,sizeof(head));tot=1;memset(fro,0,sizeof(fro));
for(int i=1;i<=m;i++)addedge(S,num[1][i],1,0);
for(int i=1;i<=m+n-1;i++){addedge(num[n][i]+cnt,T,INF,0);addedge(num[n][i],num[n][i]+cnt,INF,a[n][i]);}
for(int i=1;i<n;i++)for(int j=1;j<=m+i-1;j++){
addedge(num[i][j],num[i][j]+cnt,INF,a[i][j]);
addedge(num[i][j]+cnt,num[i+1][j],1,0);
addedge(num[i][j]+cnt,num[i+1][j+1],1,0);
}
printf("%lld
",mcf());
/*PROBLEM 3*/
memset(head,0,sizeof(head));tot=1;memset(fro,0,sizeof(fro));
for(int i=1;i<=m;i++)addedge(S,num[1][i],1,0);
for(int i=1;i<=m+n-1;i++){
addedge(num[n][i]+cnt,T,INF,0);
addedge(num[n][i],num[n][i]+cnt,INF,a[n][i]);
}
for(int i=1;i<n;i++)for(int j=1;j<=m+i-1;j++){
addedge(num[i][j],num[i][j]+cnt,INF,a[i][j]);
addedge(num[i][j]+cnt,num[i+1][j],INF,0);
addedge(num[i][j]+cnt,num[i+1][j+1],INF,0);
}
printf("%lld
",mcf());
}
/*
2 5
2 3
3 4 5
9 10 9 1
1 1 10 1 1
1 1 10 12 1 1
66
75
77
*/
T17 运输问题
仓库作为二分图X集,零售商店作为Y集。S向Xi连容量ai费用0的边。Yi向T连容量bi费用0的边,Xi向Yi连容量INF费用Cij的边。求最小费用最大流和最大费用最大流就可以。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 205;
const int maxm = 50005;
const int INF = 0x3f3f3f3f;
struct edge{
int x,y,next,v,c;
edge(){}
edge(int _x,int _y,int _nt,int _v,int _c)
:x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[maxm<<1];
int head[maxn],tot=1;
int fro[maxn],dis[maxn];bool inq[maxn];
void addedge(int x,int y,int v,int c){
e[++tot]=edge(x,y,head[x],v, c);head[x]=tot;
e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
}
int n,m,S,T;
int a[maxn],b[maxn],g[105][105];
bool SPFA(int fg);
int mcf(int fg);
void build(){
tot=1;memset(head,0,sizeof(head));
for(int i=1;i<=m;i++)addedge(S,i,a[i],0);
for(int i=1;i<=n;i++)addedge(i+m,T,b[i],0);
for(int i=1;i<=m;i++)for(int j=1;j<=n;j++)
addedge(i,j+m,INF,g[i][j]);
// for(int i=2;i<=tot;i+=2)
// cout<<e[i].x<<" "<<e[i].y<<" "<<e[i].v<<" "<<e[i].c<<endl;
}
int main(){
scanf("%d%d",&m,&n);
S=0;T=n+m+1;
for(int i=1;i<=m;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
for(int i=1;i<=m;i++)for(int j=1;j<=n;j++)scanf("%d",&g[i][j]);
build();
printf("%d
",mcf(1));
build();
printf("%d
",mcf(-1));
}
queue<int>q;
bool SPFA(int fg){
memset(inq,0,sizeof(inq));
for(int i=S;i<=T;i++)dis[i]=INF*fg;
dis[S]=0;q.push(S);inq[S]=1;
while(!q.empty()){
int x=q.front();q.pop();inq[x]=0;
for(int v,c,y,i=head[x];i;i=e[i].next){
y=e[i].y;v=e[i].v;c=e[i].c;
if(v && dis[y]*fg > (dis[x]+c)*fg){
dis[y]=dis[x]+c;
fro[y]=i;
if(!inq[y]){
inq[y]=1;
q.push(y);
}
}
}
}
return dis[T]!=INF*fg;
}
int mcf(int fg){
int ret=0;
memset(fro,0,sizeof(fro));
while(SPFA(fg)){
int tmp=INF;
for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
ret+=tmp*dis[T];
for(int i=fro[T];i;i=fro[e[i].x])e[i].v-=tmp,e[i^1].v+=tmp;
}
return ret;
}
T18 分配问题
建图方法与T17相似。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 205;
const int maxm = 50005;
const int INF = 0x3f3f3f3f;
struct edge{
int x,y,next,v,c;
edge(){}
edge(int _x,int _y,int _nt,int _v,int _c)
:x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[maxm];
int tot,head[maxn],S,T,n,fro[maxn],dis[maxn];
int g[105][105];
bool inq[maxn];
inline void addedge(int x,int y,int v,int c){
e[++tot]=edge(x,y,head[x],v, c);head[x]=tot;
e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
}
bool SPFA(int fg);
int mcf(int fg);
void build(){
tot=1;memset(head,0,sizeof(head));
S=0;T=n*2+1;
for(int i=1;i<=n;i++){
addedge(S,i,1,0);
addedge(i+n,T,1,0);
}
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
addedge(i,j+n,INF,g[i][j]);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)scanf("%d",&g[i][j]);
build();
printf("%d
",mcf(1));
build();
printf("%d
",mcf(-1));
}
queue<int>q;
bool SPFA(int fg){
for(int i=S;i<=T;i++)dis[i]=INF*fg;
dis[S]=0;inq[S]=1;q.push(S);
while(!q.empty()){
int x=q.front();q.pop();inq[x]=0;
for(int y,c,v,i=head[x];i;i=e[i].next){
y=e[i].y;v=e[i].v;c=e[i].c;
if(v && dis[y]*fg > (dis[x]+c)*fg ){
dis[y]=dis[x]+c;
fro[y]=i;
if(!inq[y]){
inq[y]=1;q.push(y);
}
}
}
}
return dis[T]!=INF*fg;
}
int mcf(int fg){
int ret=0;
memset(fro,0,sizeof(fro));
while(SPFA(fg)){
int tmp=INF;
for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
ret+=tmp*dis[T];
for(int i=fro[T];i;i=fro[e[i].x])e[i].v-=tmp,e[i^1].v+=tmp;
}
return ret;
}
T19 负载平衡问题
供求平衡问题,费用流解决
#include<bits/stdc++.h>
using namespace std;
const int maxn = 500;
const int INF = 0x3f3f3f3f;
struct edge{
int x,y,next,v,c;
edge(){}
edge(int _x,int _y,int _nt,int _v,int _c):x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[ 2400 ];
int head[maxn],tot=1;
int dis[maxn],fro[maxn],a[maxn];
bool inq[maxn];
queue<int>q;
int S,T,n;
inline void addedge(int x,int y,int v,int c){
e[++tot]=edge(x,y,head[x],v,c);head[x]=tot;
e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
}
bool SPFA(){
for(int i=S;i<=T;i++)dis[i]=INF,inq[i]=0;
q.push(S);inq[S]=1;
dis[S]=0;
while(!q.empty()){
int x=q.front();q.pop();
inq[x]=0;
for(int y,v,c,i=head[x];i;i=e[i].next){
y=e[i].y;v=e[i].v;c=e[i].c;
if(v && dis[x]+c<dis[y]){
dis[y]=dis[x]+c;fro[y]=i;
if(!inq[y]){
inq[y]=1;q.push(y);
}
}
}
}
return dis[T]!=INF;
}
int mcf(){
int ret=0;
while(SPFA()){
int tmp=INF;
for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
ret+=tmp*dis[T];
for(int i=fro[T];i;i=fro[e[i].x])e[i].v-=tmp,e[i^1].v+=tmp;
}
return ret;
}
int main(){
scanf("%d",&n);
int sum=0;
for(int i=1;i<=n;i++){scanf("%d",&a[i]);sum+=a[i];}
sum/=n;
for(int i=1;i<=n;i++)a[i]=a[i]-sum;
S=0;T=n*2+1;
for(int i=1;i<=n;i++){
if(a[i] > 0)
addedge(S,i,a[i],0);
else
addedge(i+n,T,-a[i],0);
int t;
t=i>1?i-1:n;
addedge(i,t,INF,1);addedge(i,n+t,INF,1);
t=i<n?i+1:1;
addedge(i,t,INF,1);addedge(i,n+t,INF,1);
}
cout<<mcf()<<endl;
}
T20 深海机器人问题
网格上的边连两条,一条容量1费用为其价值。还有一条容量INF费用为0,求最大费用最大流。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 500;
const int maxm = 50005;
const int INF = 0x3f3f3f3f;
struct edge{
int x,y,next,v,c;
edge(){}
edge(int _x,int _y,int _nt,int _v,int _c)
:x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[maxm];
int tot=1,head[maxn],fro[maxn],dis[maxn],S,T,a,b,P,Q;
int num[25][25];
bool inq[maxn];
void addedge(int x,int y,int v,int c){
e[++tot]=edge(x,y,head[x],v,c);head[x]=tot;
e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
}
queue<int>q;
bool SPFA(){
for(int i=S;i<=T;i++)dis[i]=-INF;
q.push(S);dis[S]=0;inq[S]=1;
while(!q.empty()){
int x=q.front();q.pop();inq[x]=0;
for(int y,c,v,i=head[x];i;i=e[i].next){
y=e[i].y,v=e[i].v,c=e[i].c;
if(v && dis[y] < dis[x]+c){
dis[y]=dis[x]+c;
fro[y]=i;
if(!inq[y]){
inq[y]=1;q.push(y);
}
}
}
}
return dis[T]!=-INF;
}
int mcf(){
int ret=0;
while(SPFA()){
int tmp=INF;
for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
ret+=dis[T]*tmp;
for(int i=fro[T];i;i=fro[e[i].x])e[i].v-=tmp,e[i^1].v+=tmp;
}
return ret;
}
int main(){
scanf("%d%d",&a,&b);
scanf("%d%d",&P,&Q);P++;Q++;
T=0;
for(int i=1;i<=P;i++)
for(int j=1;j<=Q;j++)
num[i][j]=++T;
S=0;T++;
for(int i=1;i<=P;i++)
for(int j=1;j<=Q-1;j++){
int c;
scanf("%d",&c);
int x=num[i][j],y=num[i][j+1];
addedge(x,y,1,c);
addedge(x,y,INF,0);
}
for(int i=1;i<=Q;i++)
for(int j=1;j<=P-1;j++){
int c;
scanf("%d",&c);
int x=num[j][i],y=num[j+1][i];
addedge(x,y,1,c);
addedge(x,y,INF,0);
}
for(int i=1;i<=a;i++){
int k,x,y;
scanf("%d%d%d",&k,&x,&y);x++;y++;
addedge(S,num[x][y],k,0);
}
for(int i=1;i<=b;i++){
int k,x,y;
scanf("%d%d%d",&k,&x,&y);x++;y++;
addedge(num[x][y],T,k,0);
}
printf("%d
",mcf());
}
T21 最长K可重区间集问题
将全部出现过的坐标离散化,保存每一个区间的长度。从S到最左边点连容量为k费用为0的边,最左边的点到T连容量为K费用为0的边,全部i到i+1连容量为1费用为0的边,每一个区间的左右端点所相应的点之间连容量为1费用为区间长度的边,求最大费用最大流。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
const int maxm = 100005;
const int INF = 0x3f3f3f3f;
#define MADOKA main
struct edge{
int x,y,next,v,c;
edge(){}
edge(int _x,int _y,int _nt,int _v,int _c)
:x(_x),y(_y),next(_nt),v(_v),c(_c){}
}e[maxm];
int tot=1,head[maxn],fro[maxn],dis[maxn],n,k,S,T=0;
bool inq[maxn];
struct node{
int k,p;
}a[maxn];
int b[maxn<<1],L[maxn];
bool cmp(node x,node y){return x.k<y.k;}
inline int addedge(int x,int y,int v,int c){
e[++tot]=edge(x,y,head[x],v, c);head[x]=tot;
e[++tot]=edge(y,x,head[y],0,-c);head[y]=tot;
}
queue<int>q;
bool SPFA(){
for(int i=S;i<=T;i++)dis[i]=-INF;
dis[S]=0;inq[S]=1;q.push(S);
while(!q.empty()){
int x=q.front();q.pop();inq[x]=0;
for(int y,v,c,i=head[x];i;i=e[i].next){
y=e[i].y;v=e[i].v;c=e[i].c;
if( v && dis[y] < dis[x]+c){
dis[y]=dis[x]+c;
fro[y]=i;
if(!inq[y]){
inq[y]=1;q.push(y);
}
}
}
}
return dis[T]!=-INF;
}
int mcf(){
int ret=0;
while(SPFA()){
int tmp=INF;
for(int i=fro[T];i;i=fro[e[i].x])tmp=min(tmp,e[i].v);
ret+=tmp*dis[T];
for(int i=fro[T];i;i=fro[e[i].x])e[i].v-=tmp,e[i^1].v+=tmp;
}
return ret;
}
int MADOKA(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d%d",&b[i],&b[i+n]);L[i]=b[i+n]-b[i];
a[i].k=b[i];
a[i+n].k=b[i+n];
a[i].p=i;
a[i+n].p=i+n;
}
sort(a+1,a+1+n+n,cmp);
int cnt=0;
b[a[1].p]=++T;
for(int i=2;i<=n<<1;i++)
b[a[i].p]=a[i].k==a[i-1].k?T:++T;
T++;
S=0;
addedge(S,1,k,0);addedge(T-1,T,k,0);
for(int i=1;i<=T-2;i++)addedge(i,i+1,INF,0);
for(int i=1;i<=n;i++)addedge(b[i],b[i+n],1,L[i]);
printf("%d
",mcf());
}
T24 骑士共存问题
骑士仅仅能从白格跳到黑格,再从黑格跳到白格。所以就是求二分图最大点独立集。
二分图最大点独立集等于总点数-匹配数
我的网络流跑太慢了!!。
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
using namespace std;
const int maxn = 205;
const int INF = 0x3f3f3f3f;
int g[maxn][maxn];
int n,m,S=0,T=0;
const int mx[]={1,2,2,1,-1,-2,-2,-1};
const int my[]={-2,-1,1,2,2,1,-1,-2};
struct edge{
int x,y,next,v;
edge(){}
edge(int _x,int _y,int _nt,int _v)
:x(_x),y(_y),next(_nt),v(_v){}
}e[maxn*maxn*16];
int head[maxn*maxn],cur[maxn*maxn],tot=1,h[maxn*maxn];
inline void addedge(int x,int y,int v){
e[++tot]=edge(x,y,head[x],v);head[x]=tot;
e[++tot]=edge(y,x,head[y],0);head[y]=tot;
}
int q[maxn*maxn],l,r;
bool BFS(){
memset(h,-1,sizeof(h));
h[S]=0;
q[l=r=0]=S;
while(l<=r){
int x=q[l++];
if(x==T)return 1;
for(int v,y,i=head[x];i;i=e[i].next){
y=e[i].y,v=e[i].v;
if(v && h[y]==-1){
h[y]=h[x]+1;
if(y==T)return 1;
q[++r]=y;
}
}
}
return 0;
}
int DFS(int x,int f){
if(x==T)return f;
int used=0,tmp;
for(int v,y,i=cur[x];i;i=e[i].next){
y=e[i].y;v=e[i].v;
if(v && h[y]==h[x]+1){
tmp=DFS(y,min(v,f-used));
e[i].v-=tmp;e[i^1].v+=tmp;
used+=tmp;
if(e[i].v)cur[x]=i;
if(f==used)return f;
}
}
if(!used)h[x]=-1;
return used;
}
int maxf(){
int ret=0;
while(BFS()){
for(int i=S;i<=T;i++)cur[i]=head[i];
ret+=DFS(S,INF);
}
return ret;
}
int main(){
scanf("%d%d",&n,&m);
memset(g,0,sizeof(g));
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
g[x][y]=-1;
}
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
if(g[i][j] == 0)g[i][j]=++T;
S=0;T=n*n+1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(g[i][j]==-1)continue;
if((i+j) % 2 == 0){
addedge(S,g[i][j],1);
for(int k=0;k<8;k++){
int x=i+mx[k],y=j+my[k];
if(x<1 || x>n || y<1 || y>n)continue;
if(g[x][y]==-1)continue;
addedge(g[i][j],g[x][y],INF);
}
}else{
addedge(g[i][j],T,1);
}
}
printf("%d
",n*n-m-maxf());
}