[洛谷P2711]小行星
题目大意:有$n$颗行星,每颗行星的位置是$(x,y,z)$。每次可以消除一个面(即$x,y$或$z$坐标相等)的行星,求消除这些行星的最少次数。
题解:最小割,对于每一颗小行星,从 x 面的出点向 y 面的入点连一条容量为 inf 的边(仅为保证点之间的连通性),从 y 面的出点向 z 面的入点连容量为 inf 的边。再从超级源向每个 x 面的入点连容量为 inf 的边,
从每个 z 面的出点向超级汇连容量为 inf 的边。可以简化为从超级源 S 向每个 x 连一条权值为 1 的边。从 z 面的边到终点同理
C++ Code:
#include<cstdio> #include<cstring> using namespace std; const int inf=0x3f3f3f3f; int st,ed; int q[2500],h,t,d[2500]; int head[2500],cnt=2; struct Edge{ int to,nxt,w; }e[500000]; int n; inline int min(int a,int b){return a<b?a:b;} void add(int a,int b,int c){ e[cnt]=(Edge){b,head[a],c};head[a]=cnt; e[cnt^1]=(Edge){a,head[b],0};head[b]=cnt^1; cnt+=2; } bool bfs(){ memset(d,0,sizeof d); d[q[h=t=1]=st]=1; while (h<=t){ int x=q[h++];if (x==ed)return true; for (int i=head[x];i;i=e[i].nxt){ int to=e[i].to; if (e[i].w&&!d[to]){ d[to]=d[x]+1; q[++t]=to; } } } return d[ed]; } int dfs(int x,int low){ if (x==ed||!low)return low; int res=0,w; for (int i=head[x];i;i=e[i].nxt){ int to=e[i].to; if (d[to]==d[x]+1&&e[i].w){ w=dfs(to,min(low-res,e[i].w)); e[i].w-=w; e[i^1].w+=w; res+=w; if (res==low)return res; } } if (!res)d[x]=-1; return res; } void dinic(){ int ans=0; while (bfs())ans+=dfs(st,inf); printf("%d\n",ans); } int main(){ scanf("%d",&n); st=0,ed=2001; for (int i=0;i<n;i++){ int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y+500,inf); add(y+500,y+1000,1);add(y+1000,z+1500,inf); } for (int i=1;i<=500;i++)add(st,i,1),add(i+1500,ed,1); dinic(); return 0; }