zoukankan      html  css  js  c++  java
  • bzoj4622 [NOI 2003] 智破连环阵

    Description

    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应当尽量小

    Input

    第一行包含三个整数: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)号炸弹的平面坐标。输入数据保证随机、无误、并且必然有解。

    Output

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

    Sample Input

    Sample Input 1
    4 3 6
    0 6
    6 6
    6 0
    0 0
    1 5
    0 3
    1 1

    Sample Input 2
    10 10 45
    41 67
    34 0
    69 24
    78 58
    62 64
    5 45
    81 27
    61 91
    95 42
    27 36
    91 4
    2 53
    92 82
    21 16
    18 95
    47 26
    71 38
    69 12
    67 99
    35 94

    Sample Output

    Sample Output 1
    2

    Sample Output 2
    5
    HINT
    输出数据为NOI原数据
    输出数据由楼教主代码制作
    原题有spj 此题去掉spj 只输出最优解

    HINT 

     NOI2003 Day2 T3  感谢sxb_201上传

    正解:搜索+$dp$+二分图最大匹配。

    丧心病狂的$zjo$竟然把这题出成考试题。。我敢说这是我见过的最玄学的搜索题。。

    首先,我们发现每个炸弹肯定炸一段连续的区间。那么有一个很直观的暴力的思路,那就是枚举区间。对于每个区间,能够完全覆盖的炸弹向它连边,跑一遍二分图最大匹配就行了。

    这样显然是过不了的,所以我们要剪枝。我们假设每个炸弹可以重复使用,那么我们算出当前武器开始的所有武器最少还需几个炸弹才能消灭。

    这个用$dp$来预处理。设$can[p][i][j]$表示$p$号炸弹,是否能够炸$[i,j]$这个区间。那么设$dis[i]$表示炸$[i,m]$需要的最少炸弹,易知$dis[i]=min(dis[j]+1)$,$i<j<=m+1$且$can[p][i][j-1]=1$,$dis[m+1]=0$。这个我们预处理就能得出了。

    最优性剪枝:$now+dis[i]>=ans$则剪枝,$now$为当前炸弹数。

    可行性剪枝:我们可以每次直接在原图的基础上进行增广,如果当前点不能进行增广,就不用再往下搜索了。

    然而这些剪枝还是不足以通过全部数据,我们不妨从可行性剪枝上入手。

    我们可以尝试求出当前区间右端点的最大值$maxl$,那么显然,$maxl$及其之前的端点都是可行的。

    我们发现这样可以很大程度地优化时间。首先,我们可以从$maxl$到$l$依次枚举右端点,减少搜索量;其次,我们可以只进行一次增广,因为$can[p][i][j]>=can[p][i][j+1]$,那么右端点为$maxl$时连的边,在右端点减小时同样也会出现,并不会影响答案。

    如何求出$maxl$?首先我们要求出辅助数组$maxt[p][l]$,表示炸弹$p$从$l$开始能炸到的最远的点的编号,这个很容易预处理出来,就不再赘述。

    注意到匈牙利算法的过程,一个炸弹能够使用有两种情况。首先是这个炸弹没有出现在匹配边上;其次是这个炸弹虽然出现在匹配边上,但是它能够通过增广以后和当前区间匹配。那么我们就可以使用$bfs$来解决这个问题,求出所有能够使用的炸弹(具体操作看代码吧。。),然后不断地取$maxt[p][l]$的最大值,就能求出$maxl$了。

    这就是本题的两个重要剪枝,加上这两个剪枝以后极限数据也可以瞬间求解了。

     1 //It is made by wfj_2048~
     2 #include <algorithm>
     3 #include <iostream>
     4 #include <cstring>
     5 #include <cstdlib>
     6 #include <cstdio>
     7 #include <vector>
     8 #include <cmath>
     9 #include <queue>
    10 #include <stack>
    11 #include <map>
    12 #include <set>
    13 #define inf (1<<30)
    14 #define N (110)
    15 #define il inline
    16 #define RG register
    17 #define ll long long
    18 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
    19 
    20 using namespace std;
    21 
    22 int can[N][N][N],maxt[N][N],g[N][N],dis[N],vis[N],lk[N],mt[N],x[N],y[N],u[N],v[N],q[N],n,m,k,cnt,ans;
    23 
    24 il int gi(){
    25     RG int x=0,q=1; RG char ch=getchar();
    26     while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
    27     if (ch=='-') q=-1,ch=getchar();
    28     while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar();
    29     return q*x;
    30 }
    31 
    32 il void pre(){
    33     for (RG int i=1;i<=m;++i) x[i]=gi(),y[i]=gi();
    34     for (RG int i=1;i<=n;++i){
    35     u[i]=gi(),v[i]=gi();
    36     for (RG int j=1;j<=m;++j)
    37         g[i][j]=(u[i]-x[j])*(u[i]-x[j])+(v[i]-y[j])*(v[i]-y[j])<=k*k;
    38     }
    39     for (RG int p=1;p<=n;++p){
    40     for (RG int i=1;i<=m;++i) can[p][i][i]=g[p][i],maxt[p][i]=g[p][i]?i:i-1;
    41     for (RG int i=1;i<m;++i)
    42         for (RG int j=i+1;j<=m;++j){
    43         can[p][i][j]=can[p][i][j-1]&g[p][j];
    44         if (can[p][i][j]) maxt[p][i]=j;
    45         }
    46     }
    47     for (RG int i=m;i;--i){
    48     dis[i]=inf;
    49     for (RG int j=i;j<=m;++j)
    50         for (RG int p=1;p<=n;++p)
    51         if (can[p][i][j]) dis[i]=min(dis[i],dis[j+1]+1);
    52     }
    53     return;
    54 }
    55 
    56 il int hungry(RG int x){
    57     for (RG int v=1;v<=n;++v){
    58     if (!g[x][v] || vis[v]==cnt) continue; vis[v]=cnt;
    59     if (!lk[v] || hungry(lk[v])){ mt[x]=v,lk[v]=x; return 1; }
    60     }
    61     return 0;
    62 }
    63 
    64 il void dfs(RG int l,RG int id){
    65     if (l>m){ ans=id-1; return; } if (id-1+dis[l]>=ans) return;
    66     ++cnt; RG int LK[N],MT[N],h=0,t=0,maxl=l-1;
    67     for (RG int i=1;i<=n;++i) if (!lk[i]) vis[i]=cnt,q[++t]=i;
    68     while (h<t){
    69     RG int x=q[++h]; maxl=max(maxl,maxt[x][l]);
    70     for (RG int i=1;i<id;++i)
    71         if (g[i][x] && vis[mt[i]]!=cnt) vis[mt[i]]=cnt,q[++t]=mt[i];
    72     }
    73     memcpy(LK,lk,sizeof(LK)),memcpy(MT,mt,sizeof(MT));
    74     for (RG int i=1;i<=n;++i) g[id][i]=can[i][l][maxl]; ++cnt,hungry(id);
    75     for (RG int r=maxl;r>=l;--r){
    76     for (RG int i=1;i<=n;++i) g[id][i]=can[i][l][r]; dfs(r+1,id+1);
    77     }
    78     memcpy(lk,LK,sizeof(lk)),memcpy(mt,MT,sizeof(mt)); return;
    79 }
    80 
    81 il void work(){
    82     m=gi(),n=gi(),k=gi(),pre(),ans=n;
    83     dfs(1,1),printf("%d
    ",ans); return;
    84 }
    85 
    86 int main(){
    87     File("boom");
    88     work();
    89     return 0;
    90 }
  • 相关阅读:
    [转载]memcached完全剖析--1. memcached的基础
    I/O多路复用
    How to install the zsh shell in Linux && how to set it as a default login shell
    深入理解计算机中的 csapp.h和csapp.c
    (一)网络编程基础之套接字入门
    crx
    t
    武汉第一例肺炎病例
    C语言 ## __VA_ARGS__ 宏
    NLP之中文分词cppjieba
  • 原文地址:https://www.cnblogs.com/wfj2048/p/7123729.html
Copyright © 2011-2022 走看看