1. 程式人生 > >【JZOJ5354】【NOIP2017提高A組模擬9.9】導彈攔截【網路流】【DP】

【JZOJ5354】【NOIP2017提高A組模擬9.9】導彈攔截【網路流】【DP】

題目大意:

題目連結: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;
}