【JZOJ5354】【NOIP2017提高A组模拟9.9】导弹拦截【网络流】【DP】
题目大意:
题目链接:https://jzoj.net/senior/#main/show/5354
某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。
敌国的导弹形成了立体打击,每个导弹可以抽象成一个三维空间中的点(x; y; z)。拦截系统发射的炮弹也很好地应对了这种情况,每一发炮弹也可以视为一个三维空间中的点。
但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达三维空间中任意的点,但是以后每一发炮弹到达点的坐标(x; y; z) 的三个坐标值都必须大于前一发炮弹的对应坐标值。
某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。
输入导弹飞来的坐标,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。注意: 所有导弹都是同时飞来的。
思路:
对于第一问,很明显可以意中任意一维为关键字排序后动态规划求出。方程为
对于第二问,可以用网络流求。
如果可以从转移而来(),那么就从点向点连一条边。那么,这个图就是一个有向无环图。我们要求这个图中的最小点覆盖。
那么考虑拆点,把每一个点拆成和。若可以从转移而来,那么久从向连边。源点连向所有点,汇点由所有点连过来。然后跑一边最大流,最小点覆盖即。
代码:
#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;
}