【bzoj 2044】三维导弹拦截

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

解题思路

拓扑排序+最小路径覆盖
代码:

#include<cstdio>  
#include<algorithm>  
#include<cmath>  
#include<iostream>  
#include<cstring>  
#include<string>  
#include<cstdlib>  
#include<queue> 
using namespace std; 
struct ldx 
{ 
    int x,y,z; 
}s[1005]; 
queue <int> q; 
int hed[1005],nex[1000005],lb[1000005],ru[1000005]; 
int pa[1005]; 
int n,ans=0,lo=0; 
void add(int x,int y) 
{ 
    lo++; 
    nex[lo]=hed[x]; 
    hed[x]=lo; 
    lb[lo]=y; 
} 

int had[2005],nax[2000005],lab[2000005],cap[2000005],dep[2005]; 
int loo=-1,si=2002,t=2003,mx=2147483640; 
void add1(int x,int y,int num) 
{ 
    loo++; 
    nax[loo]=had[x]; 
    had[x]=loo; 
    lab[loo]=y; 
    cap[loo]=num; 
} 

int dfs(int x,int num)  
{  
    if(x==t || num==0) return num;  
    int c=0;  
    for(int i=had[x];i!=-1;i=nax[i])  
    if(dep[lab[i]]==dep[x]+1 && cap[i])  
    {  
        int f=dfs(lab[i],min(num,cap[i]));  
        c+=f;  
        num-=f;  
        cap[i]-=f;  
        cap[i^1]+=f;  
        if(num==0) break;  
    }  
    if(c==0) dep[x]=-1; 
    return c;  
}  

bool bfs()  
{  
    memset(dep,0,sizeof(dep));  
    queue <int> qi;  
    qi.push(si);  
    dep[si]=1;  

    while(!qi.empty())  
    {  
        int x=qi.front();  
        qi.pop();  
        for(int i=had[x];i!=-1;i=nax[i])  
        if(!dep[lab[i]] && cap[i])  
        {  
           dep[lab[i]]=dep[x]+1;   
           qi.push(lab[i]);     
        }  
    }  

    return dep[t];  
}  

int dinic_()  
{  
    int c=0;  
    while(bfs()) c+=dfs(si,mx);    
    return c;  
}  

int main() 
{ 
    memset(had,-1,sizeof(had)); 
    scanf("%d",&n);   
    for(int i=1;i<=n;i++) scanf("%d%d%d",&s[i].x,&s[i].y,&s[i].z); 

    for(int i=1;i<=n;i++) 
     for(int j=1;j<=n;j++) 
      if(s[j].x>s[i].x && s[j].y>s[i].y && s[j].z>s[i].z) 
        {add(i,j);ru[j]++;} 

    for(int i=1;i<=n;i++)  
    if(ru[i]==0) {pa[i]=1;q.push(i);} 

    while(!q.empty()) 
    { 
        int x=q.front();q.pop();         
        for(int i=hed[x];i!=0;i=nex[i]) 
          { 
             pa[lb[i]]=max(pa[lb[i]],pa[x]+1);ru[lb[i]]--; 
             if(ru[lb[i]]==0) q.push(lb[i]); 
          }   
    }   

    for(int i=1;i<=n;i++) ans=max(ans,pa[i]); 
    printf("%d\n",ans); 

    for(int i=1;i<=n;i++) 
     for(int j=1;j<=n;j++) 
      if(s[j].x>s[i].x && s[j].y>s[i].y && s[j].z>s[i].z) 
        {add1(i,j+1000,1);add1(j+1000,i,0);} 

    for(int i=1;i<=n;i++)        
      { 
        add1(si,i,1);add1(i,si,0); 
        add1(i+1000,t,1);add1(t,i+1000,0); 
      } 

    printf("%d",n-dinic_());   
    return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值