【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; }