【JZOJ5354】【NOIP2017提高A组模拟9.9】导弹拦截【网络流】【DP】

版权声明:若希望转载,在评论里直接说明即可,谢谢! https://blog.csdn.net/SSL_ZYC/article/details/85394601

题目大意:

题目链接:https://jzoj.net/senior/#main/show/5354
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。
敌国的导弹形成了立体打击,每个导弹可以抽象成一个三维空间中的点(x; y; z)。拦截系统发射的炮弹也很好地应对了这种情况,每一发炮弹也可以视为一个三维空间中的点。
但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达三维空间中任意的点,但是以后每一发炮弹到达点的坐标(x; y; z) 的三个坐标值都必须大于前一发炮弹的对应坐标值。
某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹飞来的坐标,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。注意: 所有导弹都是同时飞来的。


思路:

对于第一问,很明显可以意 x , y , z x,y,z 中任意一维为关键字排序后 O ( n 2 ) O(n^2) 动态规划求出。方程为
f [ j ] = m a x ( f [ j ] , f [ i ] + 1 ) ( x i < x j , y i < y j , z i < z j ) f[j]=max(f[j],f[i]+1)(x_i<x_j,y_i<y_j,z_i<z_j)
对于第二问,可以用网络流求。
如果 j j 可以从 i i 转移而来( x i < x j , y i < y j , z i < z j x_i<x_j,y_i<y_j,z_i<z_j ),那么就从点 i i 向点 j j 连一条边。那么,这个图就是一个有向无环图。我们要求这个图中的最小点覆盖。
那么考虑拆点,把每一个点 x x 拆成 x a x_a x b x_b 。若 j j 可以从 i i 转移而来,那么久从 i a i_a i b i_b 连边。源点 S S 连向所有点 i a ( i 1 n ) i_a(i\in1\sim n) ,汇点 T T 由所有点 i b ( i 1 n ) i_b(i\in1\sim n) 连过来。然后跑一边最大流,最小点覆盖即 n m a x f l o w n-maxflow


代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;

const int N=1010;
const int Inf=2e9;
int n,tot=1,ans,maxflow,S,T;
int f[N],head[N*2],cur[N*2],dep[N*2];

struct node
{
	int x,y,z;
}a[N];

struct edge
{
	int next,to,flow;
}e[N*N];

bool cmp(node x,node y)
{
	return x.z<y.z;
}

void add(int from,int to,int flow)
{
	e[++tot].to=to;
	e[tot].flow=flow;
	e[tot].next=head[from];
	head[from]=tot;
}

bool bfs()  //分层
{
    memset(dep,0x3f3f3f3f,sizeof(dep));
    memcpy(cur,head,sizeof(cur));  //当前弧优化
    dep[S]=0;
    queue<int> q;
    q.push(S);
    while (q.size())
    {
        int u=q.front();
        q.pop();
        for (int i=head[u];~i;i=e[i].next)
        {
            int v=e[i].to;
            if (dep[v]>dep[u]+1&&e[i].flow)  
            {
                dep[v]=dep[u]+1;
                q.push(v);
            }
        }
    }
    return dep[T]<0x3f3f3f3f;
}

int dfs(int u,int flow)
{
    int low=0;
    if(u==T)
	{
        maxflow+=flow;  //最大流
        return flow;
    }
    int used=0;
    for (int i=cur[u];~i;i=e[i].next)
	{
        int v=e[i].to;
        cur[u]=i;  //当前弧
        if (e[i].flow&&dep[v]==dep[u]+1)
		{
			low=dfs(v,min(flow-used,e[i].flow));
			if (low)
			{
				used+=low;
	            e[i].flow-=low;
	            e[i^1].flow+=low;
	            if(used==flow) break;  //流满了就不能再流了
			}
        }
    }
    return used;
}

void dinic()
{
	while (bfs())
		dfs(S,Inf);
}

int main()
{
	freopen("missile.in","r",stdin);
	//freopen("missile.out","w",stdout);
	memset(head,-1,sizeof(head));
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
	sort(a+1,a+1+n,cmp);
	for (int i=1;i<=n;i++)
		for (int j=1;j<=n;j++)
			if (a[i].x<a[j].x&&a[i].y<a[j].y&&a[i].z<a[j].z)
			{
				f[j]=max(f[j],f[i]+1);
				ans=max(ans,f[j]);
				add(i,j+n,1);  //连边
				add(j+n,i,0);
			}
	S=n*2+1;
	T=n*2+2;
	for (int i=1;i<=n;i++)
	{
		add(S,i,1);
		add(i,S,0);
		add(i+n,T,1);
		add(T,i+n,0);
	}
	dinic();
	printf("%d\n%d\n",ans+1,n-maxflow);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/SSL_ZYC/article/details/85394601