题目
传送门
题目大意:星云中有n颗行星,每颗行星的位置是(x,y,z)。每次可以消除一个面(即x,y或z坐标相等)的行星,但是由于时间有限,求消除这些行星的最少次数。
题解
又一次抄了题解。自己思考的角度就不对。我思考的建图从星星与星星之间连边跑最大流,也有想到在x y z 分别拆点,两个星星在哪个维度(xyz)相同就把相应的“分点”连接。(忽略以上)
正确的思考方式如下:
消灭星星的平面可以从x,y,z三个方向任一面消灭,即要么在x面、要么在y面、要么在z面
那么x–y–z的路径中任一经过其中的一个元素那么这个面上的点就被消灭掉了。(割的边的位置对应你去掉的是哪个面)
只需要对y上的拆点,建图方式见下图
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int maxn=500001;
const int inf=1e9;
int n,maxflow,x,y,z;
struct Edge{
int next,to,dis;
}edge[maxn<<1];
int num_edge=-1,head[maxn],cur[maxn],deep[maxn];
queue <int> q;
void add_edge(int from,int to,int dis)
{
edge[++num_edge].next=head[from];
edge[num_edge].dis=dis;
edge[num_edge].to=to;
head[from]=num_edge;
}
void add(int x,int y,int z) {add_edge(x,y,z); add_edge(y,x,0);}
bool bfs(int s,int t)
{
memset(deep,0x7f,sizeof(deep));
while (!q.empty()) q.pop();
for (int i=0; i<=n; i++) cur[i]=head[i];
deep[s]=0; q.push(s);
while (!q.empty())
{
int now=q.front(); q.pop();
for (int i=head[now]; i!=-1; i=edge[i].next)
{
int to=edge[i].to;
if (deep[to]>inf && edge[i].dis){
deep[to]=deep[now]+1;
q.push(to);
if (to==t) return 1;
}
}
}
return deep[t]<inf;
}
int dfs(int now,int t,int limit)
{
if (now==t || !limit) return limit;
int flow=0,f;
for (int i=cur[now]; i!=-1; i=edge[i].next)
{
cur[now]=i; int to=edge[i].to;
if (deep[to]==deep[now]+1 && (f=dfs(to,t,min(limit,edge[i].dis))))
{
flow+=f;
limit-=f;
edge[i].dis-=f;
edge[i^1].dis+=f;
if (!limit) break;
}
}
return flow;
}
void Dinic(int s,int t)
{
while (bfs(s,t))
maxflow+=dfs(s,t,inf);
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
int S=0,T=4*n+1;
for (int i=1; i<=n; i++) add(S,i,1);
for (int i=3*n+1; i<=4*n; i++) add(i,T,1);
for (int i=1; i<=n; i++)
{
scanf("%d%d%d",&x,&y,&z); y+=n; z+=3*n;
add(x,y,inf); add(y,y+n,1); add(y,z,inf);
}
n=T+1;
Dinic(S,T);
printf("%d\n",maxflow);
return 0;
}
总结
网络流建图方式灵活,注重自己的建图方法