【bzoj2044】三维导弹拦截 dp+二分图最大匹配

题目描述

n个物品,第i个位置有ai、bi、ci三种属性。每次可以选出满足$\ a_{p_i}<a_{p_{i+1}}\ ,\ b_{p_i}<b_{p_{i+1}}\ ,\ c_{p_i}<c_{p_{i+1}}\ $的一段序列$p_1,p_2,...,p_k$(不要求$p_1,p_2,...,p_k$的大小关系),将这些物品$p_i$消掉。问:(1)一次最多能够消掉的物品的数目。 (2)最少需要多少次操作能够把所有物品全部消掉。

输入

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

输出

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

样例输入

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

样例输出

3
2


题解

dp+二分图最大匹配

第一问按照第一维排序,然后$n^2$暴力求二维LIS即可。

第二问如果把能够先打A后打B的A向B连边,那么显然这是一个DAG图,我们要求的是它的最小路径覆盖。

这是经典的二分图建模了,说一下做法吧:

每个原图中的点拆成两个($A_i$和$A_j$),如果存在边$A\to B$,则连边$A_i\to B_j$。跑二分图最大匹配,n-最大匹配即为答案。

于是直接上匈牙利算法/dinic即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 1010
using namespace std;
struct data
{
	int a , b , c;
	bool operator<(const data x)const {return a < x.a;}
}v[N];
int f[N] , head[N] , to[N * N] , next[N * N] , cnt , vis[N] , from[N];
inline void add(int x , int y)
{
	to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
bool dfs(int x)
{
	int i;
	for(i = head[x] ; i ; i = next[i])
	{
		if(!vis[to[i]])
		{
			vis[to[i]] = 1;
			if(!from[to[i]] || dfs(from[to[i]]))
			{
				from[to[i]] = x;
				return 1;
			}
		}
	}
	return 0;
}
int main()
{
	int n , i , j , ans1 = 0 , ans2 = 0;
	scanf("%d" , &n);
	for(i = 1 ; i <= n ; i ++ ) scanf("%d%d%d" , &v[i].a , &v[i].b , &v[i].c);
	sort(v + 1 , v + n + 1);
	for(i = 1 ; i <= n ; i ++ )
	{
		f[i] = 1;
		for(j = 1 ; j < i ; j ++ )
			if(v[j].a < v[i].a && v[j].b < v[i].b && v[j].c < v[i].c)
				f[i] = max(f[i] , f[j] + 1) , add(j , i);
		ans1 = max(ans1 , f[i]);
	}
	printf("%d\n" , ans1);
	for(i = 1 ; i <= n ; i ++ )
	{
		memset(vis , 0 , sizeof(vis));
		if(dfs(i)) ans2 ++ ;
	}
	printf("%d\n" , n - ans2);
	return 0;
}

 

 

posted @ 2017-09-27 10:18  GXZlegend  阅读(458)  评论(0编辑  收藏  举报