描述
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
样例输入1
4 3 6
0 6
6 6
6 0
0 0
1 5
0 3
1 1
样例输出1
2
限制
各个测试点2秒
脑洞大开的题目
1.将所有武器分成几段,问题转化成二分图匹配问题
2.【搜索武器分成xx段,再进行二分图匹配】的时间复杂度很高,需要进行剪枝
2.1乐观估计剪枝: 假设所有炸弹都可以重复使用,如果搜索到第i个武器,发现 【炸掉i~m武器需要的最少炸弹数+已经用掉的炸弹数】>=已知的最好的答案,则没有必要再搜索下去了
令 minBoom[i]表示【在炸弹可以重复使用的情况下】炸掉i~m 武器所需要的最少炸弹数 ,minBoom[i]=min{minBoom[j+1]+1|存在炸弹k能连续炸掉i~j武器}
2.2求在当前分段情况下下一段的最大长度
令maxWeapon[k][i]表示武器k从武器i开始炸,能炸到的最远武器,
求还没有与前面所有武器段匹配的炸弹能从当前武器炸到的最多武器,此外,有些炸弹虽然与武器段匹配了,但该武器段能与其他未匹配的炸弹相连,也就是存在交替路.则这些炸弹也可能与新的武器段相匹配。交替路【从一个未匹配点出发,依次经过非匹配边、匹配边、非匹配边...形成的路径叫交替路。】
2.3因为当前分段情况下下一段的最大长度是根据交替路原理求得的,故当前段加上之前的分段都能与炸弹二分匹配。当当前段小于最大长度时,必定也存在和最大长度时一样的匹配,所以当当前段小于最大长度时不需要重复计算二分图匹配。
2.4枚举当前段长度时应该从最大长度递减,这样才可以更好的利用2.1
实验表明,不使用2.4只能过9个点
不使用2.3所有测试点也能通过
只使用2.1能通过4个测试点
所以2.2效果最强大
1 #include<iostream> 2 #include<string> 3 #include<string.h> 4 #include<queue> 5 #define M 105 //武器数 6 #define N 105 //炸弹数 7 using namespace std; 8 9 int m,n,d; 10 struct{int x,y;}boom[N],weapon[M]; 11 bool reach[M][N]={0}; //炸弹n是否能炸掉武器M 12 bool f[N][M][M]={0}; //炸弹n是否能炸掉武器i~j 13 int minBoom[M]={0}; //炸掉武器i~m最少需要的炸弹数 14 bool map[M][N]={0}; //第n个炸弹是否能炸掉第i段武器 15 int maxWeapon[N][M]={0}; //炸弹n从武器i开始炸能炸的最长长度 16 int lestT=1000; 17 int toWeapon[M]={0},toBoom[N]={0}; //记录二分图匹配 18 bool vi[N]={0}; //记录每次二分图匹配每个炸弹访问情况,避免死循环 19 bool vis[N]={0}; //记录宽度优先搜索每个炸弹的访问情况 20 21 //匹配第t段武器 22 bool find(int t){ 23 for(int i=1;i<=n;++i) 24 if(map[t][i]&&!vi[i]) 25 { 26 vi[i]=1; 27 if(toBoom[i]==0||find(toBoom[i])) 28 {toBoom[i]=t;toWeapon[t]=i;return true;} 29 } 30 return false; 31 } 32 33 //已经在tBoom段武器上使用了tBoom个炸弹,此时第一个武器是第tWeapon个 34 void findAns(int tBoom,int tWeapon){ 35 if(tWeapon>=m+1) 36 { 37 if(tBoom<lestT) lestT=tBoom; 38 return ; 39 } 40 if(minBoom[tWeapon]+tBoom>=lestT) return ; //乐观剪枝 41 42 //找出交替边,并计算最长分段 43 memset(vis,0,sizeof(vis)); 44 queue<int> Q; 45 for(int i=1;i<=n;++i) 46 if(toBoom[i]==0) 47 {Q.push(i);vis[i]=1;} 48 49 int maxL=tWeapon-1; 50 while(!Q.empty()) 51 { 52 int boom=Q.front(); 53 Q.pop(); 54 maxL=max(maxL,maxWeapon[boom][tWeapon]); 55 56 for(int k=1;k<=tBoom;++k) 57 if(map[k][boom]&&!vis[toWeapon[k]]) 58 { 59 Q.push(toWeapon[k]); 60 vis[toWeapon[k]]=1; 61 } 62 } 63 64 if(maxL==tWeapon-1) return ; 65 66 67 68 int toBoom1[N],toWeapon1[N]; 69 memcpy(toBoom1,toBoom,sizeof(toBoom)); 70 memcpy(toWeapon1,toWeapon,sizeof(toWeapon)); 71 memset(vi,0,sizeof(vi)); 72 73 for(int i=1;i<=n;++i) map[tBoom+1][i]=f[i][tWeapon][maxL]; 74 find(tBoom+1); 75 76 for(int l=maxL;l>=tWeapon;--l) 77 { 78 for(int i=1;i<=n;++i) map[tBoom+1][i]=f[i][tWeapon][l]; 79 findAns(tBoom+1,l+1); 80 } 81 memcpy(toBoom,toBoom1,sizeof(toBoom)); 82 memcpy(toWeapon,toWeapon1,sizeof(toWeapon)); 83 } 84 85 int main() 86 { 87 cin>>m>>n>>d; 88 89 for(int i=1;i<=m;++i) 90 cin>>weapon[i].x>>weapon[i].y; 91 92 for(int i=1;i<=n;++i) 93 cin>>boom[i].x>>boom[i].y; 94 95 for(int i=1;i<=m;++i) 96 for(int j=1;j<=n;++j) 97 reach[i][j]=((weapon[i].x-boom[j].x)*(weapon[i].x-boom[j].x)+(weapon[i].y-boom[j].y)*(weapon[i].y-boom[j].y)<=d*d); 98 99 for(int k=1;k<=n;k++) 100 for(int i=1;i<=m;++i) 101 { 102 f[k][i][i]=reach[i][k]; 103 for(int j=i+1;j<=m;++j) 104 f[k][i][j]=f[k][i][j-1]&&reach[j][k]; 105 } 106 107 108 109 for(int i=m;i>=1;--i) 110 { 111 minBoom[i]=N; 112 for(int j=i;j<=m;++j) 113 for(int k=1;k<=n;++k) 114 if(f[k][i][j]) 115 minBoom[i]=min(minBoom[i],minBoom[j+1]+1); 116 } 117 118 for(int k=1;k<=n;++k) 119 for(int i=m;i>=1;--i) 120 { 121 maxWeapon[k][i]=i-1; 122 for(int j=m;j>=i;--j) 123 if(f[k][i][j]) 124 {maxWeapon[k][i]=j;break;} 125 } 126 127 128 findAns(0,1); 129 130 131 cout<<lestT<<endl; 132 133 134 135 // system("pause"); 136 137 138 139 }