【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,表示实际使用的炸弹数
输入输出样例
4 3 6 0 6 6 6 6 0 0 0 1 5 0 3 1 1 0 0
2
需要将B国的武器分为连续的k段,并且这k段中的每一段都能被某个大炮攻击到.
然后就可以搜索怎么分段,然后用二分图匹配判断.
Can[s][t][i] 表示A国大炮i能否炸掉B国[s,t]中的所有武器,这个递推判断一下就好了.
MaxT[s][i]表示大炮i从s开始打,可以打到的最大编号的武器的编号.初值MaxT[s][i]=s-1.这个简单DP一下.
Dis[i]表示如果A国大炮可以重复使用,打掉从i到m的武器至少需要的大炮的数量.这个用
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 }