描述

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

0 0

样例输出1[复制]

2

限制

各个测试点2秒

来源

NOI2003 Day2 Problem3

【题解】



详细的题解参见IOI2004国家集训队论文 楼天城 《浅谈部分搜索+高效算法在搜索问题中的应用》

在百度文库里就能找到。

这里写一下缩略版的。

首先必然是一个炸弹要炸掉一个连续的武器区间,且炸弹要从第一个武器开始炸。

有可能一个连续的武器区间有多个炸弹能够都炸到。

于是转换成二分图匹配的模型

左边的点是[1..m]分割成的若干区间 右边的n个点则代表炸弹.

设can[i][j][k]表示k号炸弹能否从i号武器连续炸到j号武器

左边的点i和右边的点j有边的条件是can[li][ri][j]为true;

我们要用搜索来获取最少需要的炸弹数

每一层搜索枚举的是一个区间

这个区间要给它找一个匹配,即找一个炸弹来炸它。

当然你要保证这个区间能够被一些炸弹炸到。

因此你又得找一个maxl表示一个区间的上界(能够被炸到的上界);

然后枚举这中间的值。

有时候你还可以修改以前的匹配以获得更大的区间上界。

在搜索之前

用DP搞一个perfect[i]表示重复使用炸弹,i..m号武器最少需要多少个炸弹。

在搜索的时候可以用used+perfect[now] 和 已经获得的ans的关系来剪枝。

然后就是maxl的获取需要用到zd[i][j]表示j号炸弹从i号武器开始能够连续炸到的最大武器编号;

记住可以用求匹配的方法来更换之前的匹配以期获得更大的maxl;

注解写得比较详细点

#include <cstdio>
#include <iostream>
#include <queue> using namespace std; const int M = 110;
int m, n, k, zd[M][M], a[M], b[M], perfect[M], ans = 2100000000;
bool can[M][M][M],vis[M];
bool reach[M][M],g[M][M];
queue <int> dl; struct abc
{
int x, y;
}; abc wuqi[M], zhadan[M]; void input(int &r)
{
r = 0;
char t = getchar();
while (!isdigit(t)) t = getchar();
while (isdigit(t)) r = r * 10 + t - '0', t = getchar();
} int sqr(int x)
{
return x*x;
} bool hungary(int x)//执行匈牙利算法给某个区间求一个炸弹的匹配。
{
for (int i = 1;i <= n;i++)
if (g[x][i] && !vis[i])
{
vis[i] = true;
if (b[i] == 0 || hungary(b[i]))
{
b[i] = x;
a[x] = i;
return true;
}
}
return false;
} void sear_ch(int used, int now)
{
if (used + perfect[now] >= ans)//最优性剪枝
return;
if (now == m + 1)//如果已经把所有的武器都消灭了就记录答案退出
{
ans = used;
return;
}
int maxl = now - 1;//获取区间的上限
for (int i = 1; i <= 100; i++) vis[i] = false;
for (int i = 1;i <= n;i++)//如果之前没有用过这个炸弹来匹配
if (b[i] == 0)//就加入队列中
{
vis[i] = true;
dl.push(i);
}
while (!dl.empty())
{
int x = dl.front();
dl.pop();
if (maxl < zd[now][x])//如果用这个炸弹能够增大区间的上限就增加
maxl = zd[now][x];
for (int i = 1;i <= used;i++)//利用匈牙利算法的思路
if (g[i][x] && !vis[a[i]])//可以更改之前的匹配以期获得更大的区间上限
{//因为g[i][x]所以是可以用这个x来代替那个a[i]炸弹的。
vis[a[i]] = true;
dl.push(a[i]);
}
}
if (maxl == now - 1)//如果无法获得更大的区间上限则退出
return;
int tempa[M], tempb[M];
for (int i = 1; i <= 100; i++)//用于回溯
tempa[i] = a[i], tempb[i] = b[i];
for (int i = 1; i <= 100; i++) vis[i] = false;
used++;//枚举的区间又增加了
for (int i = 1; i <= 100; i++)//可以匹配的炸弹是那些能够从now炸到maxl的炸弹
g[used][i] = can[now][maxl][i];//当然这些匹配之后是可能发生变化的。(即可以修改)
hungary(used);//为这个区间匹配一个炸弹
for (int i = maxl; i >= now; i--)//一定要从区间的上限开始往下枚举
{//这样可以较快求出一个暂时的ans,以便我们用最优性剪枝
for (int j = 1; j <= 100; j++)//这个区间的可以匹配的点是很多的。
g[used][j] = can[now][i][j];//因为now..i <= now..maxl
sear_ch(used, i + 1);//所以can[now][i]里面为true的元素是比can[now][maxl]里为true的元素多的
//因此我们在上面那个宽搜里面可以用那些多出来的炸弹来修改原来的匹配;
}
for (int i = 1; i <= 100; i++)
a[i]=tempa[i], b[i] = tempb[i];
} int main()
{
//freopen("F:\\rush.txt", "r", stdin);
input(m); input(n); input(k);
for (int i = 1; i <= m; i++)
input(wuqi[i].x), input(wuqi[i].y);
for (int i = 1; i <= n; i++)
input(zhadan[i].x), input(zhadan[i].y);
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)//reach[i][j]表示j号炸弹是否能炸到i号武器
reach[i][j] = (sqr(wuqi[i].x - zhadan[j].x) + sqr(wuqi[i].y - zhadan[j].y)) <= sqr(k);
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
can[i][i][j] = reach[i][j];//can[i][j][k]表示k号炸弹是否能连续从i号武器炸到j号武器
for (int j = 1; j <= n; j++)
for (int k = i + 1; k <= m; k++)
can[i][k][j] = can[i][k - 1][j] && reach[k][j];
for (int j = 1; j <= n; j++)
{
zd[i][j] = i-1;//zd[i][j]表示j号炸弹从i号武器开始能够连续炸到的最大武器编号
for (int k = i; k <= m; k++)
if (can[i][k][j])
zd[i][j] = k;
}
}
perfect[m + 1] = 0;//pefect[i]表示i..m这些武器炸掉,在可以重复使用炸弹时需要多少个炸弹。
for (int i = m; i >= 1; i--)
{
int t = i;
for (int j = 1; j <= n; j++)
if (zd[i][j] > t)
t = zd[i][j];
perfect[i] = perfect[t + 1] + 1;
}
sear_ch(0, 1);
printf("%d\n", ans);
return 0;
}

最新文章

  1. 动态主机配置协议(DHCP)如何启动和关闭
  2. win7挂载NFS
  3. VUE 意淫笔记
  4. 在忘记root密码的情况下如何修改linux系统的root密码
  5. DB2 日期相减
  6. jquery获取元素的值,获取当前对象的父对象等等
  7. 2012年 蓝桥杯预赛 java 本科 题目
  8. tesseract3.02识别验证码需要注意的问题
  9. cf 621D
  10. vim与shell的切换
  11. C++中复制构造函数与重载赋值操作符总结
  12. 【ArcGIS 10.2新特性】Portal for ArcGIS新特性
  13. 根据dwarfdump、Symbolicatecrash查找错误代码
  14. spring的IOC,DI及案例详解
  15. Grunt安装中遇到的问题汇总
  16. 09_Python定义方法_Python编程之路
  17. Pytorch如何用预训练模型提取图像特征
  18. 什么是tomcat集群?
  19. pl/sql报错快速解决方法(新手推荐)
  20. 149. Max Points on a Line同一条线上的最多点数

热门文章

  1. SDUT-3364_欧拉回路
  2. 某input元素值每隔三位添加逗号跟去掉逗号
  3. 【NS2】有线和无线混合场景 (转载)
  4. JAVA高级特性--内部类
  5. day15 web前端之css
  6. ansible基础☞第一条命令
  7. mysql带有子查询的like查询
  8. Java练习 SDUT-1239_水仙花数
  9. ODT 珂朵莉树 入门
  10. IDEA 通过数据库生成entity实体类