导弹拦截(升级版)JZOJ5354(提高)
Solution
第一问很明显,是动态规划
先把每个导弹排序后,f[i]表示拦截最后一颗导弹为i的最大拦截数
f[i]=max(f[i],f[j]+1)
那么对于第二问,就要换个思路
我们可以想,在系统拦截一个导弹i后,再拦截一个导弹j,点i向j连一条有向边
这样就形成了有向无环图(DAG)
所以问题转化成了求有向图的最小覆盖
有多少条不相交的边可以覆盖整张图)
可以把原来的一个点拆成两个点(一个为入点,一个为出点)
总共分别有n个入点和n个出点形成了二分图
所以我们只需要把二分图的最大匹配ans求出来,最后把n-ans即是答案
这里我选择了匈牙利算法(其他做法也许可以),机子跑得快
要注意最好用位运算符,速度快,不然有可能过不了,我试过了
代码如下:
//blog:https://www.cnblogs.com/yjyl0098/ #include<bits/stdc++.h> using namespace std; #define maxn 1000+50 int ans; int q[maxn<<1][maxn]; int f[maxn<<1]; bool judge[maxn<<1]; struct yjyl { int x,y,z; }a[maxn]; inline bool cmp(yjyl x,yjyl y) { return x.x<y.x; } inline int read() { int X=0,w=1; char ch=0; while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();} while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar(); return X*w; } inline int max(int x,int y) { return x>y?x:y; } inline int Hg(int x) { for(int i=1;i<=q[x][0];i++) if(!judge[q[x][i]]) { judge[q[x][i]]=1; if(!f[q[x][i]]||Hg(f[q[x][i]])) { f[q[x][i]]=x; return 1; } } return 0; }//把一个点拆成两个点,跑匈牙利算法 int main() { freopen("missile.in","r",stdin); freopen("missile.out","w",stdout); int n=read(); for(int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(),a[i].z=read(); sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++) for(int j=f[i]=1;j<i;j++) if(a[i].y>a[j].y&&a[i].z>a[j].z) { f[i]=max(f[i],f[j]+1);//动态规划 q[j<<1][++q[j<<1][0]]=i<<1|1; } for(int i=1;i<=n;i++) { if(f[i]>ans) ans=f[i]; f[i]=0; } printf("%d\n",ans),ans=0; for(int i=2;i<=n<<1;i+=2) { memset(judge,0,sizeof(judge)); if(Hg(i)!=0) ans++;//ans求的就是最大匹配数 } printf("%d",n-ans);//为什么是减呢?因为多一个匹配就少一个系统 }
end