20201017B组 T2 导弹拦截
jzoj5354
题目大意
给出 n n n ( n ≤ 1000 ) (n\le 1000) (n≤1000)个三维坐标 x i , y i , z i x_i,y_i,z_i xi,yi,zi,求任意排列下的最长上升子序列和最小链覆盖
TJ
前一问显然用
O
(
n
2
)
O(n^2)
O(n2)DP暴力求,你想用
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)也是可以的
对于后一问,乍一看可以用
最小链覆盖等于最长反链
于是信誓旦旦的打了个最长不上升子序列,结果没过样例,才发现反链不是我想象中的那种东西。。。
然后考虑拆点,每有点 X − > Y X->Y X−>Y就连接 A x A_x Ax和 B y B_y By,跑匈牙利算法,最小链覆盖就等于 n n n减去匹配数。
原因是每有一个匹配就相当于把两个原图上的点连了起来,没有连的时候每个点都是一条链,连一次就可以少去一条链,二分图匹配保证了每个点的初度和入度都小于2,所以一定是链。
其实也可以写最大流跑的,具体搜索:有向图的最小链覆盖
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
const int INF=1e9+7;
using namespace std;
const int N=1e3;
struct node{
int x,y,z;
}a[N+10];
struct edge{
int st,en,next;
}E[N*N];
int n,m,f[N+10],ans1,ans2,tot,last[N+10],g[N*2+10];
bool vis[N*2+10];
void add(int x,int y){
E[++tot]=(edge){x,y,last[x]};
last[x]=tot;
}
bool cmp(node a,node b){
if(a.x!=b.x)return a.x<b.x;
if(a.y!=b.y)return a.y<b.y;
return a.z<b.z;
}
bool cmp2(node a,node b){
if(a.x!=b.x)return a.x>b.x;
if(a.y!=b.y)return a.y>b.y;
return a.z>b.z;
}
bool dfs(int x){
for(int p=last[x];p;p=E[p].next){
int y=E[p].en;
if(vis[y])continue;
vis[y]=1;
if(g[y]==-1 || dfs(g[y])){
g[y]=x;
return 1;
}
}
return 0;
}
int solve(int n){
int ret=0;
memset(g,-1,sizeof(g));
fo(i,1,n){
memset(vis,0,sizeof(vis));
ret+=dfs(i);
}
return n-ret;
}
int main(){
freopen("missile.in","r",stdin);
freopen("missile.out","w",stdout);
scanf("%d",&n);
fo(i,1,n){
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
}
sort(a+1,a+n+1,cmp);
fo(i,1,n){
f[i]=1;
fo(j,1,i-1){
if(a[j].x<a[i].x && a[j].y<a[i].y && a[j].z<a[i].z){
f[i]=max(f[i],f[j]+1);
add(i,j+n);
}
}
ans1=max(ans1,f[i]);
}
ans2=solve(n);
printf("%d\n%d\n",ans1,ans2);
return 0;
}