为了练dinic优化,再写一道网络流
原题链接:
https://www.luogu.org/problemnew/show/P1402
写了很多非常辣鸡的代码还觉得挺对的我。。。
题意简述:有n个顾客,m道菜,q个房间,顾客满意的条件是能吃到自己喜欢的菜,住自己满意的房间,求最多能让多少顾客满意。
首先建边,源点连向所有的菜,菜向喜欢这道菜的顾客连一条边,顾客向他喜欢的房间连一条边,跑最大流即可。
然而这是错的。
为什么?假设有这个情况,一位顾客同时喜欢多个房间,但是很显然,他最多只能住一个房间,也就是流过这个顾客点的流量最大为1,这就是点流量的题了,解决方法就是把顾客拆开,一个只吃菜,一个只住房间(emmm...),然后在这个两个点之间连一条容量为1的边,这样再跑最大流就是对的了。
点:
房间 1--m 人1 m+1--m+n 人2 m+n+1--m+n<<1 菜 m+n<<1+1--m+n<<1+q
#include<cstdio> #include<queue> #include<cstring> #include<iostream> using namespace std; const int inf=2147483647; void read(int &y) { y=0;char x=getchar(); while(x<‘0‘||x>‘9‘) x=getchar(); while(x>=‘0‘&&x<=‘9‘) { y=y*10+x-‘0‘; x=getchar(); } } struct edge { int u,v,w; }e[100005]; int n,m,q; int s,t=505,cnt=1; int head[1005],dis[1005]; int min(int a,int b) { return a<b?a:b; } void add(int u,int v) { e[++cnt].u=head[u];e[cnt].v=v; e[cnt].w=1;head[u]=cnt; e[++cnt].u=head[v];e[cnt].v=u; e[cnt].w=0;head[v]=cnt; } int dfs(int x,int f) { if(x==t) return f; int d,tmp=0; for(int i=head[x];i!=-1;i=e[i].u) { int nxt=e[i].v; if(dis[nxt]==dis[x]+1&&e[i].w>0) { d=dfs(nxt,min(f-tmp,e[i].w)); if(d==0) continue; e[i].w-=d; e[i^1].w+=d; tmp+=d; if(tmp==f) return tmp; } } if(!tmp) dis[x]=0; return tmp; } int bfs() { memset(dis,0,sizeof(dis)); queue<int>q; q.push(0);dis[0]=1; while(!q.empty()) { int tmp=q.front();q.pop(); for(int i=head[tmp];i!=-1;i=e[i].u) { int p=e[i].v; if(dis[p]||e[i].w<=0) continue; dis[p]=dis[tmp]+1; q.push(p); } } return dis[t]; } int dinic() { int re=0; while(bfs()) re+=dfs(0,inf); return re; } int main() { memset(head,-1,sizeof(head)); read(n);read(m);read(q); int x; for(int i=1;i<=m;i++) add(s,i); for(int i=1;i<=n;i++) add(m+i,m+n+i); for(int i=1;i<=q;i++) add(m+(n<<1)+i,t); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { read(x); if(x==1) add(j,m+i); } } for(int i=1;i<=n;i++) { for(int j=1;j<=q;j++) { read(x); if(x==1) add(m+n+i,m+(n<<1)+j); } } printf("%d",dinic()); return 0; }