[APIO2010]巡邏題解
在一個地區中有 n 個村莊,編號為 1, 2, …, n。有 n – 1 條道路連線著這些村 莊,每條道路剛好連線兩個村莊,從任何一個村莊,都可以通過這些道路到達其 他任一個村莊。每條道路的長度均為 1 個單位。 為保證該地區的安全,巡警車每天要到所有的道路上巡邏。警察局設在編號 為 1 的村莊裡,每天巡警車總是從警察局出發,最終又回到警察局。 下圖表示一個有 8 個村莊的地區,其中村莊用圓表示(其中村莊 1 用黑色的 圓表示),道路是連線這些圓的線段。為了遍歷所有的道路,巡警車需要走的距 離為 14 個單位,每條道路都需要經過兩次。
為了減少總的巡邏距離,該地區準備在這些村莊之間建立 K 條新的道路, 每條新道路可以連線任意兩個村莊。兩條新道路可以在同一個村莊會合或結束 (見下面的圖例(c))。 一條新道路甚至可以是一個環,即,其兩端連線到同一 個村莊。 由於資金有限,K 只能是 1 或 2。同時,為了不浪費資金,每天巡警車必須 經過新建的道路正好一次。
這道題其實在紫題並不算難,最初考試時候遇到了這道題之後我太水,只有k==1的分數。
首先來分析一下k1的情況,當k1時候,加一條邊,省去的相當於兩點原來的距離,所以,為了省去的儘可能多,我們考慮最長的路徑(賣關子)
最長的路徑是什麼???直徑!
所以,當k=1時,問題就簡單了(雖然我當時打了很久),我們只需求出樹的直徑在直徑,在其上建邊。若直徑長l,答案就是2(n-1)-l+12(n−1)−l+1。
於是k==1就這樣不輕鬆地解決了。
然後。。。由於不怎麼會樹的直徑,就打了個dfs
特別醜的程式碼如下:
#include <bits/stdc++.h>
#define FOR(i,n,m) for(int i=n;i<=m;++i)
#define FR(i,n,m) for(int i=n;i>=m;--i)
const int N=100010;
int n,h[N<<1],cnt;
struct node {
int next,to,w;
} e[N<<1];
#define add(u,v,w) e[++cnt]= (node){h[u],v,w};h[u]=cnt;
#define n(i) e[i].next
#define t(i) e[i].to
#define QXX(u) for(int i=h[u],v;v=t(i),i;i=n(i))
using namespace std;
int f[N],_max,k;
void dfs(int u,int fa,int w) {
f[u]=w;
QXX(u) if(v!=fa) dfs(v,u,w+e[i].w);
}
int main() {
scanf("%d",&n);
FOR(i,1,n-1) {
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w) add(v,u,w)
}
dfs(1,0,0);_max=0;
FOR(i,1,n) if(f[i]>_max) _max=f[i],k=1;
dfs(k,0,0);
FOR(i,1,n) if(f[i]>_max) _max=f[i];
cout<<_max;
return 0;
}
(這就是一個裸的樹的直徑)。
but,悲傷的是,還有k=2的存在,那麼k=2該怎麼辦呢?
可以Tarjan吧…聽大佬們講的…但是我不會…
可以修改權值,修改為0吧…但是我用的前向星也不會修改…
然後作為一個蒟蒻想到了bfs,搜出第一次經過的點,進行標記…太複雜…然後想到了在第一次跑樹的直徑的第二次dfs中對父親做一下標記,就可以很容易的處理了。
#include<bits/stdc++.h>
using namespace std;
const int N=200010;
int head[N],to[N],nextt[N],tot = 1,k,n;
int maxx,p,q,fa[N],vis[N],ans,sum,f[N];
void add(int x,int y) {
to[tot] = y;
nextt[tot] = head[x];
head[x] = tot++;
}
void dfs1(int u,int faa,int len) {
if (len > maxx) {
maxx = len;
p = u;
}
for (int i = head[u]; i; i = nextt[i]) {
int v = to[i];
if (v == faa)
continue;
dfs1(v,u,len + 1);
}
}
void dfs2(int u,int faa,int len) {
if (len > maxx) {
maxx = len;
q = u;
}
fa[u] = faa; //記錄父親
for (int i = head[u]; i; i = nextt[i]) {
int v = to[i];
if (v == faa)
continue;
dfs2(v,u,len + 1);
}
}
void dfs3(int u,int faa) {
for (int i = head[u]; i; i = nextt[i]) {
int v = to[i];
if (v == faa)
continue;
dfs3(v,u);
int temp = 1;
if (vis[u] && vis[v])
temp = -1;
ans = max(ans,f[u] + f[v] + temp);
f[u] = max(f[u],f[v] + temp);
}
}
int main() {
scanf("%d%d",&n,&k);
for (int i = 1; i < n; i++) {
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
dfs1(1,0,0);
maxx = 0;
dfs2(p,0,0);
if (k == 1) {
printf("%d\n",2 * (n - 1) - maxx + 1);
return 0;
}
while (q != 0) { //標記
vis[q] = 1;
q = fa[q];
}
dfs3(1,0);
sum = 2 * (n - 1) - maxx + 1;
printf("%d\n",sum - ans + 1);
return 0;
}
於是這道題就很輕鬆的解決了