https://www.luogu.org/problemnew/show/P1020
本题在洛谷上也著有题解,这里跟洛谷上发的基本一致,也可以到本人洛谷博客上去看
https://www.luogu.org/blog/SSL-XXY/
这道题 n2 n 2 和 nlogn n l o g n 的题解已经够多了,什么dp啊,线段树啊,贪心的都有。但是对于第二问,其实可以用增广路去求
增广路可以过第二问,数据较弱的话可以AC,不过时间不是很快,只能拿到70分。
为什么此解不能AC我却发了这篇题解,因为要去学习更多的做法去做此题,部分分的思路依旧很重要!
我们来引入一下思路
对于第一问,显然跑一遍dp就行了,第二问是问你至少配备几套系统可以拦截所有导弹,也就是用足够多的路径去覆盖这张图,那就转换成了最小路径覆盖问题
根据
每一条覆盖路径的边数=覆盖点数-1
最小路径覆盖数=顶点数-最大匹配数
关于此解为什么只能拿70分的原因如下
1.明确这是一个有向图(只能是后面的导弹比前面的低或者相等)。然后对于建图需要一遍 n2 n 2 过去,最高也只能拿100分了,这是原因之一
2.对于求增广路,这里用的是匈牙利算法,我们都知道匈牙利算法的时间复杂度较慢是 O(n3) O ( n 3 ) ,尽管通过邻接表优化后它的时间复杂度是一定高于 O(n2) O ( n 2 ) 的,这是原因之二
3.第三个原因不是因为时间,而是因为空间了,对于数据范围 n<=100000 n <= 100000 ,那么我们一共需要建 n×n n × n 条边,这显然会 MLE M L E
以上便是只能拿七十分的原因
#include
#include
#define N 30001
#define M 20000001//最多只能开这么大了
#define r(i,a,b) for(int i=a;i<=b;i++)
#define zero(a) memset(a,0,sizeof(a))//清0
using namespace std;int a[30001],n,f[30001],ans1,ans2;
struct node{int next,to;}edge[M];int l[M],tot,x,y,link[N],ans,t;bool vis[N];//邻接表以及匈牙利算法数组
void add(int u,int v){edge[++tot].to=v;edge[tot].next=l[u];l[u]=tot;}//建边
bool find(int p)//找增广路
{
for(int i=l[p];i;i=edge[i].next)//访问邻接边
if(!vis[edge[i].to])
{
vis[edge[i].to]=1;
int q=link[edge[i].to];
link[edge[i].to]=p;
if(!q||find(q)) return true;
link[edge[i].to]=q;
}
return false;
}
int main()
{
f[0]=50234568;
while(scanf("%d",&a[++n])!=EOF);--n;
r(i,1,n)//第一问直接用dp过,这里就不详细介绍了
if(f[ans1]>=a[i])
f[++ans1]=a[i];
else
{
int l=1,r=ans1;
while(l<=r)
{
int mid=(l+r)>>1;
if(f[mid]>=a[i]) l=mid+1;else r=mid-1;
}
f[l]=a[i];
}
printf("%d\n",ans1);
r(i,1,n)
r(j,i+1,n)
if(a[i]>=a[j]) add(i,j);//若可以接上,则连边
r(i,1,n)
{
zero(vis);//记得每次求增广路要清0
ans2+=find(i);
}
printf("%d",n-ans2);//输出
}