zoukankan      html  css  js  c++  java
  • 【BZOJ2044】三维导弹拦截 DP+(有上下界的)网络流

    【BZOJ2044】三维导弹拦截

    Description

    一场战争正在A国与B国之间如火如荼的展开。 B国凭借其强大的经济实力开发出了无数的远程攻击导弹,B国的领导人希望,通过这些导弹直接毁灭A国的指挥部,从而取得战斗的胜利!当然,A国人民不会允许这样的事情发生,所以这个世界上还存在拦截导弹。 现在,你是一名A国负责导弹拦截的高级助理。 B国的导弹有效的形成了三维立体打击,我们可以将这些导弹的位置抽象三维中间的点(大小忽略),为了简单起见,我们只考虑一个瞬时的状态,即他们静止的状态。 拦截导弹设计非常精良,可以精准的引爆对方导弹而不需要自身损失,但是A国面临的一个技术难题是,这些导弹只懂得直线上升。精确的说,这里的直线上升指xyz三维坐标单调上升。 给所有的B国导弹按照1至N标号,一枚拦截导弹可以打击的对象可以用一个xyz严格单调上升的序列来表示,例如: B国导弹位置:(0, 0, 0) (1, 1, 0) (1, 1, 1), (2, 2, 2) 一个合法的打击序列为:{1, 3, 4} 一个不合法的打击序列为{1, 2, 4} A国领导人将一份导弹位置的清单交给你,并且向你提出了两个最简单不过的问题(假装它最简单吧): 1.一枚拦截导弹最多可以摧毁多少B国的导弹? 2.最少使用多少拦截导弹才能摧毁B国的所有导弹? 不管是为了个人荣誉还是国家容易,更多的是为了饭碗,你,都应该好好的把这个问题解决掉!

    Input

    第一行一个整数N给出B国导弹的数目。 接下来N行每行三个非负整数Xi, Yi, Zi给出一个导弹的位置,你可以假定任意两个导弹不会出现在同一位置。

    Output

    第一行输出一个整数P,表示一枚拦截导弹之多能够摧毁的导弹数。 第二行输出一个整数Q,表示至少需要的拦截导弹数目。

    Sample Input

    4
    0 0 0
    1 1 0
    1 1 1
    2 2 2

    Sample Output

    3
    2

    HINT

    所有的坐标都是[0,10^6]的整数 
    对于30%的数据满足N < 31 
    对于50%的数据满足N < 101 
    对于100%的数据满足N < 1001

    题解:第一问暴力DP即可,下面考虑第二问。

    这题本质上是求最小路径覆盖,所以可以用有上下界的网络流(最小流)解决。这里不说如何建最小流了。不过,由于本题的特殊性质,最小流的第一次dinic一定是满流的,所以我们可以直接进行第二次dinic。第二次的建图方法如下:

    1.S -> i 容量1
    2.i' -> T 容量1
    3.i' -> i 容量1
    4.对于边<i,j> i-> j' 容量1
    n-最大流即是答案。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    #include <algorithm>
    using namespace std;
    int n,ans1,ans2,cnt,S,T;
    struct node
    {
    	int x,y,z;
    }p[1010];
    int f[1010],to[2000010],next[2000010],val[2000010],head[2010],d[2010];
    queue<int> q;
    bool cmp(const node &a,const node &b)
    {
    	return a.x<b.x;
    }
    int dfs(int x,int mf)
    {
    	if(x==T)	return mf;
    	int i,k,temp=mf;
    	for(i=head[x];i!=-1;i=next[i])
    	{
    		if(d[to[i]]==d[x]+1&&val[i])
    		{
    			k=dfs(to[i],min(temp,val[i]));
    			if(!k)	d[to[i]]=0;
    			val[i]-=k,val[i^1]+=k,temp-=k;
    			if(!temp)	break;
    		}
    	}
    	return mf-temp;
    }
    int bfs()
    {
    	while(!q.empty())	q.pop();
    	memset(d,0,sizeof(d));
    	int i,u;
    	q.push(S),d[S]=1;
    	while(!q.empty())
    	{
    		u=q.front(),q.pop();
    		for(i=head[u];i!=-1;i=next[i])
    		{
    			if(!d[to[i]]&&val[i])
    			{
    				d[to[i]]=d[u]+1;
    				if(to[i]==T)	return 1;
    				q.push(to[i]);
    			}
    		}
    	}
    	return 0;
    }
    inline void add(int a,int b,int c)
    {
    	to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++;
    	to[cnt]=a,val[cnt]=0,next[cnt]=head[b],head[b]=cnt++;
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    int main()
    {
    	n=rd(),S=0,T=2*n+1;
    	int i,j;
    	for(i=1;i<=n;i++)	p[i].x=rd(),p[i].y=rd(),p[i].z=rd();
    	sort(p+1,p+n+1,cmp);
    	memset(head,-1,sizeof(head));
    	for(i=1;i<=n;i++)
    	{
    		f[i]=1,add(S,i,1),add(i+n,i,1),add(i+n,T,1);
    		for(j=1;j<i;j++)	if(p[j].x<p[i].x&&p[j].y<p[i].y&&p[j].z<p[i].z)
    			f[i]=max(f[i],f[j]+1),add(j,i+n,1);
    		ans1=max(ans1,f[i]);
    	}
    	printf("%d
    ",ans1);
    	while(bfs())
    		ans2+=dfs(S,1<<30);
    	printf("%d",n-ans2);
    	return 0;
    }
  • 相关阅读:
    如何制作动态层分组报表
    填报表之数据留痕
    填报表中也可以添加 html 事件
    填报脚本之轻松搞定复杂表的数据入库
    在报表中录入数据时如何实现行列转换
    如何在报表中绘制 SVG 统计图
    如何用报表工具实现树状层级结构的填报表
    6.JAVA_SE复习(集合)
    JAVA_SE复习(多线程)
    数据库基本概念
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7586468.html
Copyright © 2011-2022 走看看