【21.00%】【vijos P1018】智破连环阵
描述
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;
}
【21.00%】【vijos P1018】智破连环阵的更多相关文章
- luogu P1526 [NOI2003]智破连环阵 搜索+最大匹配+剪枝
LINK:智破连环阵 考试的时候 题意没理解清楚 题目是指一个炸弹爆炸时间结束后再放另一个炸弹 而放完一个炸弹紧接另一个炸弹.题目中存在然后二字. 这样我们可以发现某个炸弹只会炸连续的一段. 但是 由 ...
- 题解-NOI2003 智破连环阵
题面 NOI2003 智破连环阵 有 \(m\) 个靶子 \((ax_j,ay_j)\) 和 \(n\) 个箭塔 \((bx_i,by_i)\).每个箭塔可以射中距离在 \(k\) 以内的靶子.第 \ ...
- bzoj4622 [NOI 2003] 智破连环阵
Description B国在耗资百亿元之后终于研究出了新式武器——连环阵(Zenith Protected Linked Hybrid Zone).传说中,连环阵是一种永不停滞的自发性智能武器.但经 ...
- [luogu1526]智破连环阵
(以下在描述复杂度时,认为$n$和$m$相同,因此一律使用$n$) 称第$i$个炸弹能匹配非空区间$[l,r]$,当且仅当$l$到$r$内所有武器都在$i$攻击范围内,且$r=m$或第$r+1$个武器 ...
- bzoj 4622: [NOI 2003] 智破连环阵【dfs+匈牙利算法】
一个炸弹炸一个区间的武器,想到二分图匹配 但是直接dfs断点显然不行,预处理出dis[i]为i到m的至多值来最优性剪枝,并且标记ok[i][j]为炸弹i可以炸到j武器,mx[i][j]为i炸弹从j武器 ...
- P1526 [NOI2003]智破连环阵
目录 题意描述 算法分析 闲话 初步分析 具体思路 剪枝一 剪枝二 剪枝三 总结一下 代码实现 预处理 剪枝一 剪枝二 剪枝三 二分图匹配 代码综合 结语 又是被楼教主虐的体无完肤的一天 题意描述 在 ...
- 每月最后一周的周六晚上21:00执行任务-crontab
0 21 * * 6 /bin/sh /root/time.sh #“6”代表周六 时间判断脚本如下: #!/bin/bash if [ "$(date -d "+7 days&q ...
- relay 2015-02-05 21:00 27人阅读 评论(0) 收藏
scanf函数是以在输入多个数值数据时,若格式控制串中没有非格式字符作输入数据之间的间隔,则可用空格,TAB或回车作间隔. C编译在碰到空格,TAB,回车或非法数据(如对"%d"输 ...
- bzoj AC倒序
Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...
随机推荐
- 【风马一族_php】NO2_php基础知识
原文来自:http://www.cnblogs.com/sows/p/5995763.html (博客园的)风马一族 侵犯版本,后果自负 回顾 什么是php以及php的发展史 搭建web服务器 apa ...
- Java 调用restful webservice & jackson
package com.bullshit.webcrawler.client.impl; import java.io.BufferedReader; import java.io.IOExcepti ...
- 瑞星推国内唯一Linux系统杀毒软件 国产操作系统还需国产安全软件保护
近来在IT领域最爆炸的新闻莫过于5月20日中央国家机关政府採购中心下发通知.要求中央机关採购所有计算机类产品不同意安装Windows 8.而改用国产Linux操作系统. 此消息一出,立马引起各界关注. ...
- Flask学习之五 用户登录
英文博客地址:http://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-v-user-logins 中文翻译地址:http:// ...
- mysql把一个表的字段update成另一个表的字段根据id
mysql把一个表的字段update成另一个表的字段根据id 1.填充activity表里面的creator字段,用org的founderid,其中activity的orgid要和org的id对应,具 ...
- 10-1 body标签里面相关的标签(列表,表单,表格)
一 列表标签<ul>,<ol>,<dl> <!DOCTYPE html> <html lang="en"> <he ...
- IDEA使用中文api鼠标提示的设置
最近都在用IDEA来练习,发现有的方面确实比eclipse好用,eclipse里面可添加中文的API 提示,对初期的我帮助很大,但是IDEA却没有找到添加的地方,一直以来还以为不支持这个功能,比较遗憾 ...
- docfx 做一个和微软一样的文档平台
开发中,有一句话叫 最不喜欢的是写文档,最不喜欢的是看别人家代码没有文档.那么世界上文档写最 la 好 ji 的就是微软了,那么微软的api文档是如何做的?难道请了很多人去写文档? 实际上微软有工具用 ...
- 04Redis入门指南笔记(内部编码规则简介)
Redis是一个基于内存的数据库,所有的数据都存储在内存中.所以如何优化存储,减少内存空间占用是一个非常重要的话题.精简键名和键值是最直观的减少内存占用的方式,如将键名very.important.p ...
- 全文索引——CONTAINS 语法
Like直接在数据据中查找可以查到所有所需记录但是会扫描整个表会影响性能CONTAINS是基于全文索引进行查询,查询结果受系统全文索引分词的方法影响查询结果会不全.Select * FROM A Wh ...