1. 程式人生 > >[APIO2010]巡邏題解

[APIO2010]巡邏題解

[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;
}

於是這道題就很輕鬆的解決了