------------------------------------------------------------------------------------

17/24

------------------------------------------------------------------------------------

    开坑了...中考前看看能不能填完(总感觉会TJ...QwQ) TJ啦23333333

  1.飞行员配对问题(二分图最大匹配

  裸匈牙利。

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
using namespace std;
struct poi{int too,pre;}e[20000];
int link[210],last[210],v[210],x,y,n,m,tot,t,ans;
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void add(int x,int y){e[++tot].too=y;e[tot].pre=last[x];last[x]=tot;}
bool dfs(int x)
{for(int i=last[x],too=e[i].too;i;i=e[i].pre,too=e[i].too)if(v[too]!=t){v[too]=t;if((!link[too])||dfs(link[too])){link[too]=x;return 1;}}return 0;
}
int main()
{read(n);read(m);read(x);read(y);while(x!=-1&&y!=-1){add(y,x);read(x);read(y);}for(int i=n+1;i<=n+m+1;i++){t++;if(dfs(i))ans++;}printf("%d\n",ans);for(int i=1;i<=n;i++)if(link[i])printf("%d %d\n",i,link[i]);
}

View Code

  2.太空飞行计划问题(最大权闭合图△

  题目大意:n个实验,m个仪器,给定实验需要的仪器(一个实验可能用多个仪器,不同实验使用的仪器可能有重复)和实验获利,仪器使用费用,求最大利润并输出方案。

  有向图,点权有正有负,有向边u->v表示选了u必须选v,选一些点使权值最大,就是最大权闭合图辣。

  建图:源点往所有实验连获利为权值的边,实验往所需仪器连inf的边,仪器往汇点连费用的边,都是正权,跑最大流,答案为实验总获利-最大流。怎么证明呢?

  设实验为A集合,仪器为B集合,那我们的答案就是【A集合中所选的点权-B集合中所选的点权】可以转化成【A集合总点权-(A集合中没有选的点权+B集合中所选的点权)】,而其中的【A集合中没有选的点权+B集合中所选的点权】就是一个割,我们要让答案最大,所以就要让这个割最小,于是就把问题转化成了最小割,最小割集流量之和==最大流,于是答案就是A集合总点权-最大流即实验总获利-最大流。

  求方案的话...为什么所有题解都是dinic,ISAP没人权啊?自己yy了一个枚举的做法,可是这题输入不友好,先留坑以后调完输出再交交看。

------------------------------------------------------------------------------------

  填坑啦!

  学习了一波别人的读入...(感觉这题的读入有省选难度(雾),为什么回车前还有空格啊淦!)

  果然自己YY的对啦。首先最小割一定是简单割,也就是割集和S与T相连,因为实验和仪器连边为inf,显然不可能割这条,那么这题求方案就是求S集和T集。我们可以发现如果要选择一个仪器那么仪器到T的边必定满流,否则做这个实验一定会亏钱,就是仪器到T的边满流的就属于T集,那所需的所有仪器在T集里的实验就属于S集辣。求是否满流就把这条边删掉跑最大流看看【未删边的最大流-删边的最大流】是否等于这条边的边权,如果是的话就说明这条边满流。我一开始一直不知道为什么不能直接判断g[u][v]是否==0,写了个对拍才知道...因为g[u][v]==0并不代表这条边一定在最大流中,最大流可能有多个,而如果这条边不是一定在最大流中说明不是一定要选这个仪器或者说选了其实会亏钱,因为一个实验要用到多个仪器,必须这个实验的所有仪器都满流才能选这个实验和这些仪器,否则会亏钱,因为一个实验的获利可以高于它所需要的某一个仪器的费用,然后让这条边满流,如果直接判g[u][v]==0的话会错误的选上这个仪器。

ISAP:

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<vector>
const int inf=2147483647;
using namespace std;
vector <int> req[120];
int g[120][120],G[120][120],dis[120],vd[120],s[120],t[120],cnt,cnt2,m,n,x,y,z,sum,ulen,num;
long long ans,summ;
bool v[1000],vv[1000];
char c[1000][10010];
int dfs(int x,int flow)
{int ret=0;if(x==sum)return flow;for(int i=0;i<=sum;i++)if(g[x][i]&&dis[x]==dis[i]+1){int t=dfs(i,min(flow-ret,g[x][i]));g[x][i]-=t;g[i][x]+=t;ret+=t;if(ret==flow)return ret;}if(dis[0]>sum)return ret;if(!--vd[dis[x]])dis[0]=sum+1;vd[++dis[x]]++;return ret;
}
int main()
{scanf("%d%d",&m,&n);sum=n+m+1;for(int i=1;i<=m;i++){scanf("%d",&z);summ+=z;g[0][i]=z;cin.getline(c[i],10000);ulen=0;while(sscanf(c[i]+ulen,"%d",&num)==1){req[i].push_back(num);if(num==0)ulen++;else while(num){num/=10;ulen++;}ulen++;}for(int j=0;j<req[i].size();j++)g[i][req[i][j]+m]=inf;}for(int i=1;i<=n;i++)scanf("%d",&z),g[i+m][sum]=z;memcpy(G,g,sizeof(g));while(dis[0]<=sum)ans+=dfs(0,inf);for(int i=1;i<=n;i++){int anss=0;memcpy(g,G,sizeof(G));memset(dis,0,sizeof(dis));memset(vd,0,sizeof(vd));g[i+m][sum]=0;while(dis[0]<=sum)anss+=dfs(0,inf);if(ans-anss==G[i+m][sum])v[i]=1;}for(int i=1;i<=m;i++){vv[i]=1;for(int j=0;j<req[i].size();j++)if(!v[req[i][j]]){vv[i]=0;break;}}for(int i=1;i<=m;i++)if(vv[i])printf("%d ",i);printf("\n");for(int i=1;i<=n;i++)if(v[i])printf("%d ",i);printf("\n");printf("%d\n",summ-ans);
}

View Code

UPD:

Dinic(bzoj1497)(同样的题):

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
using namespace std;
const int inf=1000000000,maxn=60010;
struct poi{int too,pre,cf,c;}e[500010];
int n,m,tot,sum,sumz,ans,x,y,z,front,rear,cnt;
int h[maxn],v[maxn],pre[maxn],last[maxn],dis[maxn],cur[maxn];
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void add(int x,int y,int z)
{e[++tot].too=y;e[tot].c=e[tot].cf=z;e[tot].pre=last[x];last[x]=tot;e[++tot].too=x;e[tot].pre=last[y];last[y]=tot;
}
bool bfs(int x)
{for(int i=0;i<=sum;i++)pre[i]=-1,v[i]=0,dis[i]=-1;v[x]=1;h[1]=x;front=0;rear=1;while(front!=rear){int now=h[++front];if(front==maxn)front=-1;for(int i=last[now],too=e[i].too;i;i=e[i].pre,too=e[i].too)if(!v[too]&&e[i].cf){v[too]=1;h[++rear]=too;pre[too]=i;dis[too]=dis[now]+1;if(too==sum)return 1;if(rear==maxn)rear=-1;}}return 0;
}
int dfs(int x,int f)
{int tmp,flow=0;if(x==sum)return f;for(int &i=cur[x];i;i=e[i].pre){int y=e[i].too;if(e[i].cf&&dis[y]==dis[x]+1){tmp=dfs(y,min(f-flow,e[i].cf));e[i].cf-=tmp;e[i^1].cf+=tmp;flow+=tmp;if(flow==f)return f;}}return flow;
}
void dinic()
{while(bfs(0)){for(int i=0;i<=sum;i++)cur[i]=last[i];ans+=dfs(0,inf);}
}
int main()
{tot=1;read(n);read(m);sum=n+m+1;for(int i=1;i<=n;i++)read(x),add(i+m,sum,x);for(int i=1;i<=m;i++){read(x);read(y);read(z);sumz+=z;add(0,i,z);add(i,x+m,inf);add(i,y+m,inf);}dinic();printf("%d\n",sumz-ans);
}

View Code

  3.最小路径覆盖问题(最小路径覆盖△

  题目大意:n个点的DAG图,求最少几条路径可以覆盖所有的点并输出路径。

   对于有向边u->v把u拆成u和u',v拆成v和v',u向v'连边,跑二分图最大匹配,n-最大匹配数即为答案,易证。

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
using namespace std;
struct poi{int pre,too;}e[20000];
int link[1000],v[1000],n,m,ans,ru[1000],tot,last[1000],t,x,y;
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void add(int x,int y){e[++tot].too=y;e[tot].pre=last[x];last[x]=tot;}
bool dfs(int x)
{for(int i=last[x];i;i=e[i].pre)if(v[e[i].too]!=t){v[e[i].too]=t;if((!link[e[i].too])||dfs(link[e[i].too])){link[e[i].too]=x;return 1;}}return 0;
}
void dfs2(int x)
{printf("%d ",x);for(int i=last[x];i;i=e[i].pre)if(link[e[i].too]==x){dfs2(e[i].too-n);    break;}
}
int main()
{read(n);read(m);ans=n;for(int i=1;i<=m;i++)read(x),read(y),add(x,y+n);for(int i=1;i<=n;i++){t++;ans-=dfs(i);}for(int i=1;i<=n;i++)if(!link[i+n])dfs2(i),printf("\n");printf("%d\n",ans);
}

View Code

  4.魔术球问题(最小路径覆盖△

  题目大意:n个栈,要1,2,3,...依次往栈里塞数,一个栈里相邻的数之和必须是完全平方数,问最多塞几个数并输出方案。

  这题其实是上一题的强化版...一个栈里一串数可以抽象成一条路径,那就可以把问题转换成求最小路径数不大于n的最多有几个数了。枚举答案(这个最大只有1600左右),枚举到哪一个数就再枚举前面的数,把和是完全平方数的两个数连边,然后更新最小路径覆盖的答案,显然只需要再dfs一次新加进来的数就行了,最后最小路径数大于n就break,break时的最小路径数-1即为答案,求方案的话和上题一样。

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
struct poi{int pre,too;}e[20000];
int link[3000],v[2000],n,m,ans,tot,last[2000],t,x,y,cnt;
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void add(int x,int y){e[++tot].too=y;e[tot].pre=last[x];last[x]=tot;}
bool dfs(int x)
{for(int i=last[x];i;i=e[i].pre)if(v[e[i].too]!=t&&e[i].too<=cnt){v[e[i].too]=t;if((!link[e[i].too])||dfs(link[e[i].too])){link[e[i].too]=x;return 1;}}return 0;
}
void dfs2(int x)
{printf("%d ",x);for(int i=last[x];i;i=e[i].pre)if(link[e[i].too]==x&&cnt>=e[i].too){dfs2(e[i].too);    break;}
}
int main()
{read(n);cnt=1;int ans=dfs(1);while(1){cnt++;for(int i=1;i<cnt;i++){int x=(int)sqrt(i+cnt);if(x*x==(i+cnt))add(cnt,i);}t++;ans+=dfs(cnt);if(cnt-ans>n)break;}printf("%d\n",--cnt);ans=0;memset(link,0,sizeof(link));for(int i=1;i<=cnt;i++)t++,ans+=dfs(i);for(int i=1;i<=cnt;i++)if(!link[i])dfs2(i),printf("\n");
}

View Code

  5.圆桌问题(最大流

  题目大意:m个单位,每个单位有ri个人,要坐n张桌子,每张可以坐ci个人,每张桌子不能坐同个单位的人,输出方案。

  一眼题,1A真酸爽...

  S连m个单位容量ri,每个单位连每个桌子容量1,每个桌子连T容量ri,最大流。

  方案就枚举单位和桌子看看这条边是否满流。

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
using namespace std;
int dis[100010],vd[100010],g[1000][1000],sum,n,m,x,summ,ans;
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
int dfs(int x,int flow)
{int ret=0;if(x==sum)return flow;for(int i=0;i<=sum;i++)if(g[x][i]&&dis[x]==dis[i]+1){int t=dfs(i,min(flow-ret,g[x][i]));g[x][i]-=t;g[i][x]+=t;ret+=t;if(ret==flow)return ret;}if(dis[0]>sum)return ret;if(!--vd[dis[x]])dis[0]=sum+1;vd[++dis[x]]++;return ret;
}
int main()
{read(m);read(n);sum=n+m+1;for(int i=1;i<=m;i++)read(x),g[0][i]=x,summ+=x;for(int i=1;i<=n;i++)read(x),g[i+m][sum]=x;for(int i=1;i<=m;i++)for(int j=1;j<=n;j++)g[i][j+m]=1;while(dis[0]<=sum)ans+=dfs(0,2147483647);if(ans==summ){printf("1\n");for(int i=1;i<=m;i++){for(int j=1;j<=n;j++)if(!g[i][j+m])printf("%d ",j);printf("\n");}}else printf("0\n");
}

View Code

  6.最长递增子序列问题(最多不相交路径(最大流△

  题目大意:①求一个序列的最长不下降子序列长度k,②求最多能取出(取出的不能再用)多少个长度为n的不下降子序列,③如果a1和an能取出多次求最多可以取出多少个。

  这题PowerOJ上WA了但是其他OJ都过了,而且我把黄学长的程序交上去也WA了...干脆当自己过了。

  第一问:DP求出每一位后面的最长不下降子序列f[i],懒得写nlogn了。

  第二问:i拆成i和i',i连i'容量为1。若f[i]==k,S连i容量为1。若f[i]==1,i'连T容量为1。若i<j&&a[i]<=a[j]&&f[i]==f[j]+1,i'连j容量为1。最大流。

  第三问:符合条件的1和n,把和它取出次数有关的边的容量改为inf,跑最大流。

  注意:对于一个最长不下降子序列长度为1的序列,第三问答案是序列长度而不是inf。

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
using namespace std;
const int inf=233333333,maxn=1010;
struct poi{int c,f,cf;}g[maxn][maxn],G[maxn][maxn];
struct poii{int too,pre;}e[100010];
int n,a[maxn],f[maxn],last[maxn],h[10100],pre[maxn],ans,sum,anss,tot;
bool v[maxn];
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void add(int x,int y)
{e[++tot].too=y;e[tot].pre=last[x];last[x]=tot;e[++tot].too=x;e[tot].pre=last[y];last[y]=tot;}
void bfs(int s)
{int front=0,rear=1;for(int i=0;i<=sum;i++)v[i]=0,pre[i]=-1;v[s]=1;h[1]=s;while(front!=rear){if(front>1000)front=-1;int now=h[++front];for(int i=last[now],too=e[i].too;i;i=e[i].pre,too=e[i].too)if(g[now][too].cf&&!v[too]){pre[too]=now;v[too]=1;rear>1000&&(rear=-1);h[++rear]=too;}}
}
void dinic(int s,int t)
{bfs(s);while(pre[t]!=-1){int mincf=inf;for(int i=pre[t],j=t;i!=-1;j=i,i=pre[i])mincf=min(mincf,g[i][j].cf);for(int i=pre[t],j=t;i!=-1;j=i,i=pre[i]){g[i][j].f+=mincf;g[j][i].f=-g[i][j].f;g[i][j].cf-=mincf;g[j][i].cf+=mincf;}ans+=mincf;bfs(s);}
}
int main()
{read(n);for(int i=1;i<=n;i++)read(a[i]);for(int i=n;i>=1;i--){int mx=0;for(int j=i+1;j<=n;j++)if(a[i]<=a[j])mx=max(mx,f[j]);f[i]=mx+1;anss=max(anss,f[i]);}printf("%d\n",anss);sum=n*2+1;for(int i=1;i<=n;i++){add(i,i+n);g[i][i+n].c=g[i][i+n].cf=1;if(f[i]==anss)g[0][i].c=g[0][i].cf=1,add(0,i);if(f[i]==1)g[i+n][sum].c=g[i+n][sum].cf=1,add(i+n,sum);for(int j=i+1;j<=n;j++)if(a[i]<=a[j]&&f[i]==f[j]+1)g[i+n][j].c=g[i+n][j].cf=1,add(i+n,j);}memcpy(G,g,sizeof(g));dinic(0,sum);printf("%d\n",ans);if(anss==1)printf("%d\n",n);else{memcpy(g,G,sizeof(G));ans=0;if(f[1]==anss)g[0][1].c=g[0][1].cf=g[1][1+n].c=g[1][1+n].cf=inf,add(0,1),add(1,n+1);if(f[1]==1)g[1][1+n].c=g[1][1+n].cf=g[1+n][sum].c=g[1+n][sum].cf=inf,add(1,n+1),add(n+1,sum);if(f[n]==anss)g[0][n].c=g[0][n].cf=g[n][n+n].c=g[n][n+n].cf=inf,add(0,n),add(n,n*2);if(f[n]==1)g[n+n][sum].c=g[n+n][sum].cf=g[n][n+n].c=g[n][n+n].cf=inf,add(n*2,sum),add(n,n*2);dinic(0,sum);printf("%d\n",ans);}
}

View Code

  7.试题库问题(最大流

   题目大意:一张试卷需要k个类型的题,每个类型需要ai道题,题库里有n道题,一道题可以属于多个类型,输出一个出题的方案。

  S连k个类型容量为ai,k个类型连可以属于这个类型的题目容量为1,每个题目连T容量为1,最大流,方案判满流。

  这题的数据较大,我估计ISAP过不去,因为在二分图里dinic效率高很多,于是我学习了一波dinic... woc,这不就是费用流把spfa改成bfs么,于是我直接把自己的费用流改过来了,然后一看其他博客的代码,发现我改出来的dinic把他们的dfs都去掉了,在bfs时记录路径直接一路扫过来,这样也吼兹磁啊,而且如果记得dinic费用流就不会忘,记得费用流dinic就不会忘(所以忘了一个两个就都忘了?)。

  UPD:mdzz原来费用流改过来的dinic慢的要死,直接被bzoj1497卡成傻逼。。。这玩意儿每次只会跑一条增广路,但是dinic建完图会把能跑的都跑了,所以费用流改过来的dinic建了无数次图,但dinic只建了几次。。。顺便学习了一波当前弧优化,就是比原来的程序多加了个&,但是快了好多2333。因为bzoj1497和费用流24题第2差不多,就把代码丢上面了。

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
const int maxn=2000,inf=2147483647;
struct poi{int c,f,cf;}g[maxn][maxn];
int h[1010],pre[maxn],ans,n,m,x,y,z,k,sum,p;
char ch[maxn][maxn];
bool v[maxn];
using namespace std;
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void bfs(int s)
{int front=0,rear=1;for(int i=0;i<=sum;i++)v[i]=0,pre[i]=-1;v[s]=1;h[1]=s;while(front!=rear){if(front>1000)front=-1;int now=h[++front];for(int i=0;i<=sum;i++)if(g[now][i].cf&&!v[i]){pre[i]=now;v[i]=1;rear>1000&&(rear=-1);h[++rear]=i;}}
}
void dinic(int s,int t)
{bfs(s);while(pre[t]!=-1){int mincf=inf;for(int i=pre[t],j=t;i!=-1;j=i,i=pre[i])mincf=min(mincf,g[i][j].cf);for(int i=pre[t],j=t;i!=-1;j=i,i=pre[i]){g[i][j].f+=mincf;g[j][i].f=-g[i][j].f;g[i][j].cf-=mincf;g[j][i].cf+=mincf;}ans+=mincf;bfs(s);}
}
int main()
{read(k);read(n);sum=n+k+1;for(int i=1;i<=k;i++)read(x),g[0][i].c=g[0][i].cf=x,m+=x;for(int i=1;i<=n;i++){g[i+k][sum].c=g[i+k][sum].cf=1;read(p);for(int j=1;j<=p;j++)read(x),g[x][i+k].c=g[x][i+k].cf=1;}dinic(0,sum);if(ans<m)printf("No Solution!");else{for(int i=1;i<=k;i++){printf("%d:",i);for(int j=1;j<=n;j++)if(!g[i][j+k].cf&&g[i][j+k].c)printf(" %d",j);printf("\n");}}
}

View Code

  8.机器人路径规划问题(不可做,先跳

  9.方格取数问题(最大独立集△

  题目大意:n*m的矩阵取出一些数使得两两不相邻且和最大。

  最大独立集=总点权-最小点覆盖=总点权-最小割=总点权-最大流。

  染个色可以发现是二分图,于是分成A集和B集,S连A容量为点权,A连B容量inf,B连T容量点权。

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
using namespace std;
const int inf=233333333,maxn=100;
struct poi{int c,cf,f;}g[maxn][maxn];
int n,m,x,h[1010],pre[maxn],sum,ans,summ;
bool v[maxn];
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void bfs(int s)
{int front=0,rear=1;for(int i=0;i<=sum;i++)v[i]=0,pre[i]=-1;v[s]=1;h[1]=s;while(front!=rear){if(front>1000)front=-1;int now=h[++front];for(int i=0;i<=sum;i++)if(g[now][i].cf&&!v[i]){pre[i]=now;v[i]=1;rear>1000&&(rear=-1);h[++rear]=i;}}
}
void dinic(int s,int t)
{bfs(s);while(pre[t]!=-1){int mincf=inf;for(int i=pre[t],j=t;i!=-1;j=i,i=pre[i])mincf=min(mincf,g[i][j].cf);for(int i=pre[t],j=t;i!=-1;j=i,i=pre[i]){g[i][j].f+=mincf;g[j][i].f=-g[i][j].f;g[i][j].cf-=mincf;g[j][i].cf+=mincf;}ans+=mincf;bfs(s);}
}
int num(int x,int y){return (x-1)*m+y;}
int main()
{read(n);read(m);sum=n*m+1;for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){read(x);summ+=x;if((i+j)&1){g[0][num(i,j)].c=g[0][num(i,j)].cf=x;if(j-1>0)g[num(i,j)][num(i,j-1)].c=g[num(i,j)][num(i,j-1)].cf=inf;if(i-1>0)g[num(i,j)][num(i-1,j)].c=g[num(i,j)][num(i-1,j)].cf=inf;if(i+1<=n)g[num(i,j)][num(i+1,j)].c=g[num(i,j)][num(i+1,j)].cf=inf;if(j+1<=m)g[num(i,j)][num(i,j+1)].c=g[num(i,j)][num(i,j+1)].cf=inf;}else g[num(i,j)][sum].c=g[num(i,j)][sum].cf=x;}dinic(0,sum);printf("%d\n",summ-ans);
}

View Code

  10.餐巾计划问题(费用流△

  题目大意:有N天,每天需要ai张餐巾,用完可以送去快洗店洗m天费用f或慢洗店洗n天费用s或者买新的一条费用p,洗完或者买新的才能继续用,问N天的最小费用。

  把没洗和需要的分成两个集合A和B,S连Ai容量为ai费用0,Ai连A(i+1)容量为inf,费用为0表示留着餐巾,Ai连B(i+m)容量为inf,费用f表示送快洗店,Ai连B(i+n)费用s表示送慢洗店,S连Bi容量inf费用p表示买新的餐巾,Bi连T容量为ai费用0。跑费用流。

  循环队列记得开大点QAQ...

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
const int maxn=2020,inf=1000000000;
struct poi{int c,f,cf,v;}g[maxn][maxn];
struct poii{int too,pre;}e[100010];
int h[2010],dist[maxn],pre[maxn],last[maxn],sum,ans,n,m,tot,p,f,nn,s,x;
bool v[maxn];
using namespace std;
void add(int x,int y){e[++tot].too=y;e[tot].pre=last[x];last[x]=tot;}
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void spfa(int s)
{int front=0,rear=1;for(int i=0;i<=sum;i++)dist[i]=inf,v[i]=0,pre[i]=-1;dist[s]=0;v[s]=1;h[1]=s;while(front!=rear){if(front>2000)front=-1;int now=h[++front];for(int i=last[now],too=e[i].too;i;i=e[i].pre,too=e[i].too){if(g[now][too].cf==0)continue;if(g[now][too].v==inf)g[now][too].v=-g[too][now].v;if(dist[too]>dist[now]+g[now][too].v){dist[too]=dist[now]+g[now][too].v;pre[too]=now;if(!v[too])v[too]=1,rear>2000&&(rear=-1),h[++rear]=too;}}v[now]=0;}
}
void ford(int s,int t)
{spfa(s);while(pre[t]!=-1){int mincf=inf;for(int i=pre[t],j=t;i!=-1;j=i,i=pre[i])mincf=min(mincf,g[i][j].cf);ans+=dist[t]*mincf;for(int i=pre[t],j=t;i!=-1;j=i,i=pre[i]){g[i][j].f+=mincf;g[j][i].f=-g[i][j].f;g[i][j].cf-=mincf;g[j][i].cf+=mincf;}spfa(s);}
}
void insert(int x,int y,int c,int v)
{g[x][y].c=g[x][y].cf=c;g[x][y].v=v;add(x,y);add(y,x);
}
int main()
{read(n);read(p);read(m);read(f);read(nn);read(s);sum=n*2+1;for(int i=0;i<=sum;i++)for(int j=0;j<=sum;j++)g[i][j].v=inf;for(int i=1;i<=n;i++){read(x);if(i<n)insert(i,i+1,inf,0);if(i+m<=n)insert(i,i+m+n,inf,f);if(i+nn<=n)insert(i,i+nn+n,inf,s);insert(0,i,x,0);insert(0,i+n,inf,p);insert(i+n,sum,x,0);}ford(0,sum);printf("%d\n",ans);
}

View Code

  11.航空路线问题(最长不相交路径(最大费用最大流△

  题目大意:求两条a->b长度和最长的不相交路径

  这题好坑啊...输出方案特别麻烦,写了我一天,最后还一直WA一个点,看题解说是a->b有边的话就可以算两条,改了还是WA一个点,先留坑。

  拆点连边为1,对于边u->v,u'连v容量1。跑最大费用最大流。

  方案跑一遍dfs,或者把spfa的条件改为满流再跑费用流记录路径也行,但是不能第一次费用流就记录,因为有反向弧会往回跑,记录出来的路径会GG。

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
const int maxn=510,inf=1000000000,mod=19961993;
struct poii{int too,pre,c,f,cf,v;}e[100010];
int h[2010],dist[maxn],pre[maxn],last[maxn],ha[19961993],fir[maxn],sec[maxn];
int sum,ans,n,m,tot,anss1,anss2,x,anss,cnt1,cnt2,flag,fflag;
bool v[maxn];
char s[maxn][maxn],s1[maxn],s2[maxn];
using namespace std;
void add(int x,int y,int c,int v)
{e[++tot].too=y;e[tot].pre=last[x];last[x]=tot;e[tot].c=e[tot].cf=c;e[tot].v=v;e[++tot].too=x;e[tot].pre=last[y];last[y]=tot;e[tot].v=-v;
}
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void spfa(int s)
{int front=0,rear=1;for(int i=0;i<=sum;i++)dist[i]=-inf,v[i]=0,pre[i]=-1;dist[s]=0;v[s]=1;h[1]=s;while(front!=rear){if(front>2000)front=-1;int now=h[++front];for(int i=last[now],too=e[i].too;i;i=e[i].pre,too=e[i].too){if(e[i].cf)if(dist[too]<dist[now]+e[i].v){dist[too]=dist[now]+e[i].v;pre[too]=i;if(!v[too])v[too]=1,rear>2000&&(rear=-1),h[++rear]=too;}}v[now]=0;}
}
void ford(int s,int t)
{spfa(s);while(pre[t]!=-1){int mincf=inf,cnt=0;;for(int i=pre[t];i!=-1;i=pre[e[i^1].too])mincf=min(mincf,e[i].cf);ans+=mincf;anss+=dist[t];for(int i=pre[t];i!=-1;i=pre[e[i^1].too]){e[i].f+=mincf;e[i^1].f=-e[i].f;e[i].cf-=mincf;e[i^1].cf+=mincf;}spfa(s);}
}
int hashh(int x)
{int len=strlen(s[x]),sum=0;for(int i=0;i<len;i++)sum=(sum*10+s[x][i])%mod;return sum;
}
bool dfs(int x,int fa,int t)
{if(t==1&&x==n)return 1;if(t==2&&x==n+1)return 1;int num=0,xx=0;if(t==1)num=x+n,xx=x;else num=x-n,xx=x-n;if(xx!=1)v[xx]=v[xx+n]=1;for(int i=last[num];i;i=e[i].pre){if(e[i].too==xx||e[i].too==xx+n)continue;int cf=t==1?e[i].cf:e[i^1].cf;if(cf==0&&(e[i].too!=fa)&&(!v[e[i].too])){int next=e[i].too>n?e[i].too-n:e[i].too;if(t==1&&xx==1&&next==n){fflag=1;continue;}//if(t==2)printf("%d %d\n",e[i].too,cf);if(t==1&&next>xx)if(dfs(e[i].too,x,t)){fir[++cnt1]=xx;return 1;}if(t==2&&next<xx)if(dfs(e[i].too,x,t)){sec[++cnt2]=xx;return 1;}}}return 0;
}
int main()
{tot=1;read(n);read(m);sum=n*2;for(int i=1;i<=n;i++){scanf("%s",s[i]);ha[hashh(i)]=i;if(i!=1&&i!=n)add(i,i+n,1,0);else add(i,i+n,2,0);}for(int i=1;i<=m;i++){scanf("%s%s",s[n+1],s[n+2]);int x=ha[hashh(n+1)],y=ha[hashh(n+2)];if(x>y)swap(x,y);if(x==1&&y==n)flag=1;x=x+n;add(x,y,1,1);}ford(1,sum);memset(v,0,sizeof(v));if(ans==2){printf("%d\n",anss);dfs(1,-1,1);dfs(n*2,-1,2);for(int i=cnt1;i>=1;i--)printf("%s\n",s[fir[i]]);if(!cnt1&&fflag)printf("%s\n",s[1]);for(int i=cnt2;i>=1;i--)printf("%s\n",s[sec[i]]);printf("%s\n",s[1]);}else if(flag)printf("2\n%s\n%s\n%s\b",s[1],s[n],s[1]);else printf("No Solution!");
}

View Code

  12.软件补丁问题(spfa

  题目大意不好说...直接戳链接看题吧。

  这为什么会在网络流24题里...

  一开始以为是状压,结果发现有后向性不能dp,哇其实写个spfa就可以了?

  学会新姿势,“~”是按位取反。这样从a中除去b就简单多啦!a&(~b)

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int maxn=250,inf=1000000000,maxnum=2100000;
struct poi{int pos,dis;};
priority_queue<poi>q;
int n,m,dist[maxnum],f1[maxn],f2[maxn],b1[maxn],b2[maxn],t[maxn];
bool v[maxnum];
char s1[maxn],s2[maxn];
bool operator <(poi a,poi b){return a.dis>b.dis;};
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void spfa(int x)
{for(int i=0;i<=x;i++)dist[i]=inf;v[x]=1;dist[x]=0;q.push((poi){x,0});while(!q.empty()){int now=q.top().pos;q.pop();for(int i=1;i<=m;i++)if(((now&b1[i])==b1[i])&&(!(now&b2[i]))){int too=now&(~f1[i])|f2[i];if(dist[too]>dist[now]+t[i]){dist[too]=dist[now]+t[i];if(!v[too])v[too]=1,q.push((poi){too,dist[too]});}}v[now]=0;}
}
int main()
{read(n);read(m);for(int i=1;i<=m;i++){read(t[i]);scanf("%s%s",s1,s2);for(int j=0;j<n;j++){if(s1[j]=='+')b1[i]|=(1<<j);if(s1[j]=='-')b2[i]|=(1<<j);}for(int j=0;j<n;j++){if(s2[j]=='-')f1[i]|=(1<<j);if(s2[j]=='+')f2[i]|=(1<<j);}}spfa((1<<n)-1);if(dist[0]==inf)printf("0");else printf("%d\n",dist[0]);
}

View Code

  13.星际转移问题(分层图最大流△

  感觉一个星期没写博客概括能力--,题目大意戳链接看吧。

  这题的思路好妙啊,把题目分成d天来处理,把图分成d个层次。

  首先0向1连容量为k的边,表示要送走k个人。枚举天数d(这里有个误区,不能二分,因为网络流是一直保持最大流状态,d+1后很快就能跑出新网络的最大流),把每个空间站拆成d个点分别表示第1~d天的空间站,把飞船d-1天停留的空间站(d-1,i)与d天停留的空间站(d,j)连边,容量为hi,每个空间站与自己前一天的空间站连边容量为inf,(d,-1)的最大流为k,就输出d。

  写这题GG了好多次,忘记建双向边,下次要注意(其实是模板忘改了)。。。

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
const int maxn=1000,inf=1000000000;
struct poii{int too,pre,c,f,cf,x;}e[100010];
int h[1010],pre[maxn],last[maxn],r[maxn][maxn],fa[maxn],hh[maxn],d,ans,n,m,x,y,z,tot,sum,k;
char ch[maxn][maxn];
bool v[maxn];
using namespace std;
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void add(int x,int y,int z)
{e[++tot].too=y;e[tot].c=e[tot].cf=z;e[tot].pre=last[x];last[x]=tot;e[++tot].too=x;e[tot].c=e[tot].cf=0;e[tot].pre=last[y];last[y]=tot;
}
void bfs(int s)
{int front=0,rear=1;for(int i=0;i<=sum;i++)v[i]=0,pre[i]=-1;v[s]=1;h[1]=s;while(front!=rear){if(front>1000)front=-1;int now=h[++front];for(int i=last[now],too=e[i].too;i;i=e[i].pre,too=e[i].too){if(e[i].cf&&!v[too]){pre[too]=i;v[too]=1;rear>1000&&(rear=-1);h[++rear]=too;}}}
}
void dinic(int s,int t)
{bfs(s);while(pre[t]!=-1){int mincf=inf;for(int i=pre[t];i!=-1;i=pre[e[i^1].too])mincf=min(mincf,e[i].cf);for(int i=pre[t];i!=-1;i=pre[e[i^1].too]){e[i].f+=mincf;e[i^1].f=-e[i].f;e[i].cf-=mincf;e[i^1].cf+=mincf;}ans+=mincf;bfs(s);}
}
int gf(int v){return fa[v]==v?v:fa[v]=gf(fa[v]);}
int num(int x,int y){return (x)*(n+2)+y;}
int main()
{tot=1;read(n);read(m);read(k);for(int i=1;i<=n+2;i++)fa[i]=i;for(int i=1;i<=m;i++){read(hh[i]);read(r[i][0]);for(int j=1;j<=r[i][0];j++){read(r[i][j]);r[i][j]+=2;if(j!=1)fa[gf(r[i][j])]=gf(r[i][j-1]);}}if(gf(1)!=gf(2)){printf("0");return 0;}add(0,2,k);d=-1;while(ans<k){d++;sum+=n+2;if(d!=0){for(int i=1;i<=n+2;i++)add(num(d-1,i),num(d,i),inf);for(int i=1;i<=m;i++)add(num(d-1,r[i][(d-1)%(r[i][0])+1]),num(d,r[i][d%(r[i][0])+1]),hh[i]);}dinic(0,1+d*(n+2));}printf("%d\n",d);
}

View Code

  14.孤岛营救问题(spfa+状态压缩

  怎么我的网络流里又混进了奇怪的东西...

  dist[i][state]表示走到位置i,钥匙的状态为state

  总是忘记无解输出-1,下次要注意...

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<queue>
using namespace std;
const int maxn=1000,inf=1000000000;
struct poi{int dis,pos,state;};
struct zs{int pre,too;}e[100010];
priority_queue<poi>q;
bool operator <(poi a,poi b){return a.dis>b.dis;};
int n,m,p,k,J,mn,tot,x1,y1,x2,y2,z;
int last[maxn],g[maxn][maxn],dist[maxn][2100],dxy[5]={0,0,0,-1,+1};
bool v[maxn][2100];
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void add(int x,int y){e[++tot].too=y;e[tot].pre=last[x];last[x]=tot;}
int num(int x,int y){return (x-1)*m+y;}
void spfa()
{for(int i=1;i<=n*m;i++)for(int j=0;j<=(1<<(J+1))-1;j++)dist[i][j]=inf;int st=0;for(int i=last[1];i;i=e[i].pre)st|=(1<<e[i].too);dist[1][st]=0;v[1][st]=1;q.push((poi){0,1,st});while(!q.empty()){poi now=q.top();q.pop();for(int i=1;i<=4;i++){int too=now.pos+dxy[i];if(too>n*m||too<1||((now.pos%m==0)&&dxy[i]==1)||((now.pos%m==1)&&dxy[i]==-1)||g[now.pos][too]==-1)continue;st=now.state;for(int j=last[too];j;j=e[j].pre)st|=(1<<e[j].too);if(dist[too][st]>dist[now.pos][now.state]+1&&((now.state&g[now.pos][too])==g[now.pos][too])){dist[too][st]=dist[now.pos][now.state]+1;if(!v[too][st]){v[too][st]=1;q.push((poi){dist[too][now.state],too,st});}}}v[now.pos][now.state]=0;}
}
int main()
{read(n);read(m);read(p);read(k);dxy[1]=-m;dxy[2]=m;for(int i=1;i<=k;i++){read(x1);read(y1);read(x2);read(y2);read(z);if(z==0)g[num(x1,y1)][num(x2,y2)]=g[num(x2,y2)][num(x1,y1)]=-1;else g[num(x1,y1)][num(x2,y2)]=g[num(x2,y2)][num(x1,y1)]=1<<z;}read(J);for(int i=1;i<=J;i++){read(x1);read(y1);read(z);add(num(x1,y1),z);}spfa();mn=inf;for(int i=0;i<=(1<<(J+1))-1;i++)mn=min(mn,dist[n*m][i]);if(mn==inf)printf("-1");else printf("%d\n",mn);
}

View Code

  15.汽车加油行驶问题(spfa+状态

  MDZZ干脆叫最短路24题好了...

  dist[i][state]表示走到位置i,油量为state

  凭什么走到加油站就必须加油...调了好久才发现自己看错题

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<queue>
using namespace std;
const int maxn=50010,inf=1000000000;
struct poi{int dis,pos,state;};
priority_queue<poi>q;
bool operator <(poi a,poi b){return a.dis>b.dis;};
int n,k,mn,a,b,c;
int dist[maxn][20],g[maxn],dxy[5]={0,0,0,-1,+1};
bool v[maxn][20];
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
int num(int x,int y){return (x-1)*n+y;}
void spfa()
{for(int i=1;i<=n*n;i++)for(int j=0;j<=k;j++)dist[i][j]=inf;dist[1][k]=0;v[1][k]=1;q.push((poi){0,1,k});while(!q.empty()){poi now=q.top();q.pop();for(int i=1;i<=4;i++){int too=now.pos+dxy[i];int st=now.state;if(too>n*n||too<1||((now.pos%n==0)&&dxy[i]==1)||((now.pos%n==1)&&dxy[i]==-1))continue;int cost=0;if(i==1||i==3)cost=b;if((!g[too])&&dist[too][st-1]>dist[now.pos][st]+cost&&st>0){dist[too][st-1]=dist[now.pos][st]+cost;if(!v[too][st-1]){v[too][st-1]=1;q.push((poi){dist[too][st-1],too,st-1});}}if(g[too]&&dist[too][k]>dist[now.pos][st]+a+cost&&st>0){dist[too][k]=dist[now.pos][st]+a+cost;if(!v[too][k]){v[too][k]=1;q.push((poi){dist[too][k],too,k});}}if((!g[too])&&dist[too][k]>dist[now.pos][st]+a+c+cost&&st>0){dist[too][k]=dist[now.pos][st]+a+c+cost;if(!v[too][k]){v[too][k]=1;q.push((poi){dist[too][k],too,k});}}}v[now.pos][now.state]=0;}
}
int main()
{read(n);read(k);read(a);read(b);read(c);dxy[1]=-n;dxy[2]=n;for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)read(g[num(i,j)]);spfa();mn=inf;for(int i=0;i<=k;i++)mn=min(mn,dist[n*n][i]);printf("%d\n",mn);
}

View Code

  16.数字梯形问题(最长不相交路径(最大费用最大流△

  题目大意:规则1:路径互不相交 规则2:路径只可以在点相交 规则3:路径可以在点和边相交。从梯形顶部到底部分别找三个规则的最长路径。

  和第11题差不多,就是把一个点拆成俩来限制点的使用次数。

  PS:这题规则2和3的顶部的点不能用多次...T_T

  规则1:S连顶部容量1费用0,点i拆成i和i',i连i'容量1费用权值,i'连下一个点j容量1费用0,底部点连T容量1费用0。

  规则2:S连顶部容量1费用0,点i拆成i和i',i连i'容量inf费用权值,i'连下一个点j容量1费用0,底部点连T容量inf费用0。

  规则3:S连顶部容量1费用0,点i拆成i和i',i连i'容量inf费用权值,i'连下一个点j容量inf费用0,底部点连T容量inf费用0。

  然后跑最大费用最大流就行辣!

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
const int maxn=1100,inf=1000000000;
struct poii{int too,pre,c,f,cf,v;}e[100010];
int h[maxn],dist[maxn],pre[maxn],last[maxn],z[maxn][maxn];
int sum,sum1,ans,n,m,tot;
bool v[maxn];
using namespace std;
void add(int x,int y,int c,int v)
{e[++tot].too=y;e[tot].pre=last[x];last[x]=tot;e[tot].c=e[tot].cf=c;e[tot].v=v;e[++tot].too=x;e[tot].pre=last[y];last[y]=tot;e[tot].v=-v;
}
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void spfa(int s)
{int front=0,rear=1;for(int i=0;i<=sum;i++)dist[i]=-inf,v[i]=0,pre[i]=-1;dist[s]=0;v[s]=1;h[1]=s;while(front!=rear){if(front>maxn)front=-1;int now=h[++front];for(int i=last[now],too=e[i].too;i;i=e[i].pre,too=e[i].too)if(e[i].cf)if(dist[too]<dist[now]+e[i].v){dist[too]=dist[now]+e[i].v;pre[too]=i;if(!v[too])v[too]=1,rear>maxn&&(rear=-1),h[++rear]=too;}v[now]=0;}
}
void ford(int s,int t)
{spfa(s);while(pre[t]!=-1){int mincf=inf;for(int i=pre[t];i!=-1;i=pre[e[i^1].too])mincf=min(mincf,e[i].cf);ans+=mincf*dist[t];for(int i=pre[t];i!=-1;i=pre[e[i^1].too]){e[i].f+=mincf;e[i^1].f=-e[i].f;e[i].cf-=mincf;e[i^1].cf+=mincf;}spfa(s);}
}
int num(int x,int y){return (2*m+x-2)*(x-1)/2+y;}
int main()
{tot=1;read(m);read(n);sum1=num(n,m+n-1);sum=sum1*2+1;for(int i=1;i<=m;i++)add(0,i,1,0);for(int i=1;i<=n;i++)for(int j=1;j<m+i;j++){read(z[i][j]);add(num(i,j),num(i,j)+sum1,1,z[i][j]);if(i!=n){add(num(i,j)+sum1,num(i+1,j),1,0);add(num(i,j)+sum1,num(i+1,j+1),1,0);}else add(num(i,j)+sum1,sum,1,0);}ford(0,sum);printf("%d\n",ans);ans=0;memset(e,0,sizeof(e));memset(last,0,sizeof(last));tot=1;for(int i=1;i<=m;i++)add(0,i,1,0);for(int i=1;i<=n;i++)for(int j=1;j<m+i;j++){add(num(i,j),num(i,j)+sum1,inf,z[i][j]);if(i!=n){add(num(i,j)+sum1,num(i+1,j),1,0);add(num(i,j)+sum1,num(i+1,j+1),1,0);}else add(num(i,j)+sum1,sum,inf,0);}ford(0,sum);printf("%d\n",ans);ans=0;memset(e,0,sizeof(e));memset(last,0,sizeof(last));tot=1;for(int i=1;i<=m;i++)add(0,i,1,0);for(int i=1;i<=n;i++)for(int j=1;j<m+i;j++){add(num(i,j),num(i,j)+sum1,inf,z[i][j]);if(i!=n){add(num(i,j)+sum1,num(i+1,j),inf,0);add(num(i,j)+sum1,num(i+1,j+1),inf,0);}else add(num(i,j)+sum1,sum,inf,0);}ford(0,sum);printf("%d\n",ans);
}

View Code

  17.运输问题(最小/最大费用流(真·模板题

  S连A容量a[i]费用0,A连B容量inf费用cij,B连T容量b[i]费用0,最小/最大费用最大流。  

#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int inf=1000000000,maxn=2010;
struct poi{int pre,too,v,c,f,cf;}e[200010],E[200010];
struct poii{int dis,pos;};
priority_queue<poii>q;
bool operator <(poii a,poii b){return a.dis<b.dis;};
int n,m,z,sum,ans,tot,front,rear;
int dist[maxn],h[maxn],pre[maxn],last[maxn],a[maxn],b[maxn];
bool v[maxn];
void read(int &k)
{k=0;int f=1;char c=getchar();while(c<'0'||c>'9')c=='-'&&(f=-1),c=getchar();while(c<='9'&&c>='0')k=k*10+c-'0',c=getchar();k*=f;
}
void add(int x,int y,int c,int v)
{e[++tot].too=y;e[tot].pre=last[x];last[x]=tot;e[tot].cf=e[tot].c=c;e[tot].v=v;e[++tot].too=x;e[tot].pre=last[y];last[y]=tot;e[tot].v=-v;
}
void spfa(int s,int ty)
{for(int i=0;i<=sum;i++){if(ty)dist[i]=inf;else dist[i]=-inf;pre[i]=-1;v[i]=0;}v[s]=1;front=0;rear=1;h[1]=s;dist[s]=0;while(front!=rear){int now=h[++front];if(front==maxn)front=-1;for(int i=last[now];i;i=e[i].pre)if(e[i].cf){if(ty){if(dist[e[i].too]>dist[now]+e[i].v){dist[e[i].too]=dist[now]+e[i].v;pre[e[i].too]=i;if(!v[e[i].too]){v[e[i].too]=1;h[++rear]=e[i].too;if(rear==maxn)rear=-1;}}}else{if(dist[e[i].too]<dist[now]+e[i].v){dist[e[i].too]=dist[now]+e[i].v;pre[e[i].too]=i;if(!v[e[i].too]){v[e[i].too]=1;h[++rear]=e[i].too;if(rear==maxn)rear=-1;}}}}v[now]=0;}
}
void ford(int s,int t,int ty)
{spfa(s,ty);while(pre[t]!=-1){int mincf=inf;for(int i=pre[t];i!=-1;i=pre[e[i^1].too])mincf=min(mincf,e[i].cf);ans+=dist[t]*mincf;for(int i=pre[t];i!=-1;i=pre[e[i^1].too]){e[i].f+=mincf;e[i^1].f=-e[i].f;e[i].cf-=mincf;e[i^1].cf+=mincf;}spfa(s,ty);}
}
int main()
{tot=1;read(m);read(n);sum=n+m+1;for(int i=1;i<=m;i++)read(a[i]),add(0,i,a[i],0);for(int i=1;i<=n;i++)read(b[i]),add(i+m,sum,b[i],0);for(int i=1;i<=m;i++)for(int j=1;j<=n;j++)read(z),add(i,j+m,inf,z);memcpy(E,e,sizeof(e));ford(0,sum,1);printf("%d\n",ans);ans=0;memcpy(e,E,sizeof(E));ford(0,sum,0);printf("%d\n",ans);
}

View Code

  18.分配问题

  19.负载平衡问题

  20.深海机器人问题

  21.最长k可重区间集问题

  22.最长k可重线段集问题

  23.火星探险问题

  24.骑士共存问题

总结(持续更新中...):

  1.最大权闭合图:

    有向图,点权有正有负,有向边u->v表示选了u必须选v,选一些点使权值最大。

    S连正权点容量为权值,对于有向边u->v,u连v容量inf,负权点连T容量为权值相反数,答案为正权点总权值-最大流。

  2.最小路径覆盖:

    每个点只能在一条路径(不是边)上,求最少几条路径覆盖所有的点,一个点也可以算一条路径。

    每个点u拆成u和u',对于有向边u->v,u'连v,答案为总点数减去二分图最大匹配。

    无向图答案为总点数-(二分图最大匹配/2)。

  3.最多不相交(或每个点最多为k条线段交点)路径:

    求长度为k的不相交路径最多有几条。

    每个点i拆成i和i‘相连容量为1,S连合法的起点容量为1,可以相连接的点相连容量为1,合法的终点连T容量为1,最大流。

    若可以相交且某个点最多作为k条线段交点,就修改i和i'边的容量为k。

  4.二分图最小点(权)覆盖:

    找出最少的点使得这些点和所有边都有关联。

    最小点覆盖直接最大匹配,最小点覆盖数=最大匹配数。

    最小点权覆盖,A集和B集,S连A容量为点权,A连B容量inf,B连T容量点权。最大流即为答案。 

  5.二分图最大(点权)独立集

    选一些点要求互不相连且权值最大。

    最大独立集=总点数-最小点覆盖。

    最大点权独立集=总点权-最小点权覆盖。

  6.最大团:最大独立集的对立,先留坑。

    选一些点要求两两都有边相连且权值最大。

  7.线性规划网络优化:

    将问题分成几个集合,把集合间的关系变成边跑费用流。

  8.最长不相交(或每个点最多为k条线段交点)路径:

    求k条不相交的路径使总长度最长。

    拆点连边为k,对于边u->v,u'连v容量inf(或者k)。跑最大费用最大流。

    若可以相交且某个点最多作为k条线段交点,就修改i和i'边的容量为k。

    边可以相交就修改u'到v的边的容量。

  9.分层图网络流

    对于一个问题可以划分成几个相同的层次,比如每一天的每个图。

    枚举层次,边枚举边建图跑网络流解决问题。

  10.有条件最短路

    其实和分层图最短路一个原理,给dist加一维记录状态,有时候要状态压缩。

    适用于 需要某种条件才能到达某个点 或者 行走需要满足某些条件 的最短路题目。

转载于:https://www.cnblogs.com/Sakits/p/6850995.html

【算法】【网络流24题】巨坑待填(成功TJ,有时间再填)相关推荐

  1. 解题报告:线性规划与网络流24题

    目录 A.飞行员配对方案问题 (二分图最大匹配)(最大流)[提高+/省选- ] B.太空飞行计划问题(最大权闭合图转最小割.最小割方案输出)[省选/NOI- ] C.最小路径覆盖问题(有向无环图最小路 ...

  2. 【线性规划与网络流24题】孤岛营救问题 分层图

    孤岛营救问题 Time Limit: 1 Sec  Memory Limit: 128 MB Description 1944年,特种兵麦克接到国防部的命令.要求马上赶赴太平洋上的一个孤岛,营救被敌军 ...

  3. Cogs 727. [网络流24题] 太空飞行计划(最大权闭合子图)

    [网络流24题] 太空飞行计划 ★★☆ 输入文件:shuttle.in 输出文件:shuttle.out 简单对比 时间限制:1 s 内存限制:128 MB [问题描述] W 教授正在为国家航天中心计 ...

  4. 【线性规划与网络流24题】汽车加油行驶问题 分层图

    汽车加油行驶问题 Time Limit: 1 Sec  Memory Limit: 128 MB Description 给定一个 N*N的方形网格,设其左上角为起点◎,坐标为( 1,1),X轴向右为 ...

  5. 734. [网络流24题] 方格取数问题 二分图点权最大独立集/最小割/最大流

    «问题描述: 在一个有m*n 个方格的棋盘中,每个方格中有一个正整数.现要从方格中取数,使任 意2 个数所在方格没有公共边,且取出的数的总和最大.试设计一个满足要求的取数算法. «编程任务: 对于给定 ...

  6. P2756 飞行员配对方案问题【网络流24题】

    P2756 飞行员配对方案问题 文章目录 题目背景 题解: 代码: 题目背景 第二次世界大战期间,英国皇家空军从沦陷国征募了大量外籍飞行员.由皇家空军派出的每一架飞机都需要配备在航行技能和语言上能互相 ...

  7. 软件补丁问题(网络流24题)

    洛谷题目链接:软件补丁问题 题目背景 none! 题目描述 T 公司发现其研制的一个软件中有 n 个错误,随即为该软件发放了一批共 m 个补丁程序.每一个补丁程序都有其特定的适用环境,某个补丁只有在软 ...

  8. liberOJ#6006. 「网络流 24 题」试题库 网络流, 输出方案

    #6006. 「网络流 24 题」试题库   题目描述 假设一个试题库中有 n nn 道试题.每道试题都标明了所属类别.同一道题可能有多个类别属性.现要从题库中抽取 m mm 道题组成试卷.并要求试卷 ...

  9. [网络流24题] 最长递增子序列

    [网络流24题] 最长递增子序列 «问题描述: 给定正整数序列x1,..., xn. (1)计算其最长递增子序列的长度s. (2)计算从给定的序列中最多可取出多少个长度为s的递增子序列. (3)如果允 ...

最新文章

  1. myeclipse下拷贝的项目,tomcat下部署名称和导出为war包的名称默认值修改
  2. LeetCode 7. 整数反转 Reverse Integer 官网答案的条件判定的一点思考
  3. 最详细的NLP开源数据集分享--包含342个公开数据集
  4. 一条进程的栈区、堆区、数据区和代码区在内存中的映射
  5. java 字节 操作_实例解析Java byte数组操纵方式代码
  6. 性能测试真如你想象的那么简单?
  7. python中sort返回值_Python函数你真的都学会了吗?来看看这篇Python高阶函数!
  8. 【转】Asp.net控件开发学习笔记整理篇 - 数据回传
  9. 6.5 开始进入设计 … Transition to Design
  10. linux虚拟主机_云服务器与虚拟主机的区别
  11. Odoo10教程---模块化二:模型间关系,继承,计算字段等
  12. 编程语言的通用概念[共同特征]
  13. ubuntu 9.10学习笔记
  14. FPGA按键消抖—两种按键消抖形式的对比
  15. Python接口自动化
  16. 深圳房价链家数据分析
  17. java登陆密码加密怎么做,Java如何实现密码加密
  18. matlab 画折线图,matlab 双坐标折线图画法
  19. 【Web技术】919- 前端关于单点登录的知识
  20. IOS常用第三方库《转》

热门文章

  1. 元素与集合的问题思考
  2. scala入门-07特质类(trait)的使用
  3. JS 动态显示 获取下拉框的多个值
  4. daily scrum 11.9
  5. Effective C# Item33:限制类型的可见性
  6. 打印屏蔽部分内容window.print()
  7. 安卓错误: 找不到R符号_国际音标及附加符号读音amp;用法示例
  8. mysql连接池设计_数据库连接池(基于MySQL数据库)
  9. (95)分频器设计(偶数分频)
  10. 超级计算机操作系统有什么不同,超级计算机功能强大吗?它与普通计算机不同,但也使用Windows系统...