【NOI2003】智破连环阵

题目描述

B国在耗资百亿元之后终于研究出了新式武器——连环阵(Zenith Protected Linked Hybrid Zone)。传说中,连环阵是一种永不停滞的自发性智能武器。但经过A国间谍的侦察发现,连环阵其实是由M个编号为1,2,…,M的独立武器组成的。最 初,1号武器发挥着攻击作用,其他武器都处在无敌自卫状态。以后,一旦第i(1<=i< M)号武器被消灭,1秒种以后第i+1号武器就自动从无敌自卫状态变成攻击状态。当第M号武器被消灭以后,这个造价昂贵的连环阵就被摧毁了。

为了彻底打击B国科学家,A国军事部长打算用最廉价的武器——炸弹来消灭连环阵。经过长时间的精密探测,A国科学家们掌握了连环阵中M个武器的平面 坐标,然后确定了n个炸弹的平面坐标并且安放了炸弹。每个炸弹持续爆炸时间为5分钟。在引爆时间内,每枚炸弹都可以在瞬间消灭离它平面距离不超过k的、处 在攻击状态的B国武器。和连环阵类似,最初a1号炸弹持续引爆5分钟时间,然后a2号炸弹持续引爆5分钟时间,接着a3号炸弹引爆……以此类推,直到连环 阵被摧毁。

显然,不同的序列a1、a2、a3...消灭连环阵的效果也不同。好的序列可以在仅使用较少炸弹的情况下就将连环阵摧毁;坏的序列可能在使用完所有 炸弹后仍无法将连环阵摧毁。现在,请你决定一个最优序列a1、a2、a3…使得在第ax号炸弹引爆的时间内连环阵被摧毁。这里的x应当尽量小。

输入输出格式

输入格式:

第一行包含三个整数:M、n和k(1<=M, n<=100,1<=k<=1000),分别表示B国连环阵由M个武器组成,A国有n个炸弹可以使用,炸弹攻击范围为k。以下M行,每 行由一对整数xi,yi(0<=xi,yi<=10000)组成,表示第i(1<=i<=M)号武器的平面坐标。再接下来n行, 每行由一对整数ui,vi(0<=ui,vi<=10000)组成,表示第i(1<=i<=n)号炸弹的平面坐标。输入数据保证 随机、无误、并且必然有解。

输出格式:

一行包含一个整数x,表示实际使用的炸弹数

输入输出样例

输入样例#1:
4 3 6
0 6
6 6
6 0
0 0
1 5
0 3
1 1
0 0
输出样例#1:
2


需要将B国的武器分为连续的k,并且这k段中的每一段都能被某个大炮攻击到.

然后就可以搜索怎么分段,然后用二分图匹配判断.

Can[s][t][i] 表示A国大炮i能否炸掉B[s,t]中的所有武器,这个递推判断一下就好了.

MaxT[s][i]表示大炮is开始打,可以打到的最大编号的武器的编号.初值MaxT[s][i]=s-1.这个简单DP一下.

Dis[i]表示如果A国大炮可以重复使用,打掉从im的武器至少需要的大炮的数量.这个用
DP求出,状态转移方程为:dis[i]=1+dis[j]其中,j=max(MaxT[i][k]).

这个东西用来最优性剪枝,若当前已用的大炮+dis[当前已经攻击了的武器+1]>最优解则剪枝.

我们在枚举区间长度时,可以先用O(n^2)的广搜求出最大可攻击到的武器编号maxL,显然编号小于等于maxL的武器都可以被攻击到.

怎么求maxL?

设二分图的A数组表示武器区间,B数组表示大炮.

用广搜的方法,若某个点i为未匹配点,那么可以使用;若某个点i为匹配点,且存在一条从未匹配点的交错路,则这个点也是可以使用的,因为这条匹配边增广之后就不是匹配边了.每次出队的时候更新maxL.

然后连边,增广,搜索下一个区间.

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<cstring>
 5 #include<string>
 6 #include<algorithm>
 7 #include<map>
 8 #include<complex>
 9 #include<queue>
10 #include<stack>
11 #include<cmath>
12 #include<set>
13 #include<vector>
14 using namespace std;
15 int reach[110][110],n,m,x1[110],x2[110],Y1[110],y2[110],a[110],b[110];
16 bool can[110][110][110],g[110][110],vis[110];
17 int MaxT[110][110],dis[110],ans=1999999999;//MaxT[s][i]炸弹i从s开始可炸到的最大编号.
18 void prepare(){
19   for(int s=1;s<=m;s++){
20     for(int i=1;i<=n;i++)
21       can[s][s][i]=reach[s][i];
22     for(int t=s+1;t<=m;t++)
23       for(int i=1;i<=n;i++)
24     can[s][t][i]=can[s][t-1][i] && reach[t][i];
25     for(int i=1;i<=n;i++){
26       MaxT[s][i]=s-1;
27       for(int t=s;t<=m;t++)
28         if(can[s][t][i])
29           MaxT[s][i]=t;
30     }
31   }
32   dis[m+1]=0;
33   for(int s=m;s>=1;s--){
34     int t=s-1;
35     for(int i=1;i<=n;i++)
36       if (MaxT[s][i]>t)
37     t=MaxT[s][i];
38     dis[s]=1+dis[t+1];
39   }
40 }
41 bool find(int v){//匈牙利算法
42   for(int i=1;i<=n;i++)
43     if(g[v][i] && !vis[i]){
44       vis[i]=1;
45       if(b[i]==0 || find(b[i])){
46     a[v]=i;
47     b[i]=v;
48     return 1;
49       }
50     }
51   return 0;
52 }
53 void search(int now,int s){
54   if(now+dis[s]>=ans) return;
55   if(s==m+1) {ans=now;return;}
56   int tmpa[110],tmpb[110];
57   queue<int>Q;
58   memset(vis,0,sizeof(vis));
59   int maxL=s-1;
60   for(int i=1;i<=n;i++)
61     if(b[i]==0) vis[i]=1,Q.push(i);
62   while(!Q.empty()){
63     int u=Q.front();
64     Q.pop();
65     maxL=max(maxL,MaxT[s][u]);
66     for(int i=1;i<=now;i++)
67       if(g[i][u] && !vis[a[i]])
68     vis[a[i]]=1,Q.push(a[i]);
69   }
70   memcpy(tmpa,a,sizeof(a));memcpy(tmpb,b,sizeof(b));
71   memset(vis,0,sizeof(vis));
72   now++;
73   for(int i=1;i<=n;i++)
74     g[now][i]=can[s][maxL][i];
75   find(now);//增广
76   for(int t=maxL;t>=s;t--){
77     for(int i=1;i<=n;i++)
78       g[now][i]=can[s][t][i];
79     search(now,t+1);
80   }
81   memcpy(a,tmpa,sizeof(a));memcpy(b,tmpb,sizeof(b));
82 }
83 int main(){
84   int R;//m个武器,n个炸弹
85   scanf("%d%d%d",&m,&n,&R);
86   for(int i=1;i<=m;i++) scanf("%d%d",&x1[i],&Y1[i]);
87   for(int i=1;i<=n;i++) scanf("%d%d",&x2[i],&y2[i]);
88   for(int i=1;i<=m;i++)
89     for(int j=1;j<=n;j++)
90       reach[i][j]=((x1[i]-x2[j])*(x1[i]-x2[j])+(Y1[i]-y2[j])*(Y1[i]-y2[j])<=R*R);
91   prepare();
92   search(0,1);
93   printf("%d",ans);
94   return 0;
95 }

 

 

 

posted @ 2017-07-07 07:21  嘘丶  阅读(746)  评论(2编辑  收藏  举报