1. 程式人生 > >bzoj4622 [NOI 2003] 智破連環陣

bzoj4622 [NOI 2003] 智破連環陣

noi iostream 探測 mem das 解決 註意 最大值 好的

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\n",ans); return;
84 }
85 
86 int main(){
87     File("boom");
88     work();
89     return 0;
90 }

bzoj4622 [NOI 2003] 智破連環陣