导弹拦截的另解

导弹拦截是很老很经典的dp入门题了,它的主要方法如n^2dp和队列优化都是很入门的方法。

但是如果导弹拦截问题转到多维空间(如三维),即维度不只有高度,还可能有多个参数限制时,这个问题又该如何解决呢?

这时候需要用到图论的知识了。

第一问是最长序列,与正常n^2dp的做法一样。但是第二问并不能更换符号再次dp来实现(包括队列优化也不行,可以自己试一下)。这时我们注意到,导弹拦截的过程只可能不断向后拦截,不可能拦截完后面的导弹在回头拦截前面的导弹,所以可以将能够连续拦截的导弹之间建立有向边(前指向后),可以通过前面发现的特点证明它一定是一个DAG(有向无环图)。这时候想要找到最少系统数,即求DAG上的最小覆盖。而DAG的最小覆盖=点数-最大匹配数(二分图匹配基本定理),所以这道题转换成了求二分图最大匹配。方法也很直观,将每颗导弹拆为2个点,将能够连续拦截的导弹之间建立有向边(前的左端点指向后的右端点),然后跑一遍匈牙利就可以了。ps:因为是这一题的题解所以打的是二维的(时间与高度),但其实多维的也能一样解决。只需要更改a数组的判断就行了。

代码(内含注释):

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include//庞大的库(然而大部分都没用上)
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define Forward(i,a,b) for(i=(a);i>=(b);--i)
using namespace std;
const int MAXN=150;
template
inline void read(T &x)//没有用上却仍然挂着的读入优化
{
    T s=0,f=1;
    char k=getchar();
    while(!isdigit(k)&&k!='-')k=getchar();
    if(k=='-')
    {
        f=-1;
        k=getchar();
    }
    while(isdigit(k))
    {
        s=(s<<3)+(s<<1)+(k^'0');
        k=getchar();
    }
    x=s*f;
}
struct edge
{
    int v,next;
}p[MAXN*MAXN];//边
int n=1,a[MAXN],dp[MAXN],head[MAXN],e,dir[MAXN];//数组分别记录高度,dp数组,列式前向星的头和匈牙利的match数组
bool vis[MAXN];//记录是否访问
void add(int u,int v)//加入边
{
    p[++e].v=v;
    p[e].next=head[u];
    head[u]=e;
}
bool dfs(int x)//匈牙利,不清楚的去翻百度
{
    int v=head[x];
    while(v)
    {
        if(!vis[p[v].v])
        {
            vis[p[v].v]=true;
            if(!dir[p[v].v]||dfs(dir[p[v].v]))
            {
                dir[p[v].v]=x;
                return true;
            }
        }
        v=p[v].next;
    }
    return false;
}
int main(void)
{
    while(scanf("%d",&a[n])!=EOF)++n;//处理输入
    --n;
    a[0]=0xFFFFFFF;
    int i,j,ans=0;
    For(i,1,n)
       For(j,0,i-1)if(a[i]


你可能感兴趣的:(导弹拦截的另解)