BZOJ 1495 [NOI2006]網絡收費(暴力DP)

題意

給定一棵滿二叉樹,每個葉節點有一個狀態0/10/1,對於每兩個葉節點i,ji,j,如果這兩個葉節點狀態相同但他們的LCALCA所管轄的子樹中的與他們狀態相同的葉節點個數較少(少於1/21/2),則會產生2fij2f_{ij}的代價,如果狀態不同,則產生fijf_{ij}的代價,如果狀態相同且LCALCA管轄子樹中與他們狀態相同葉節點個數較多,則不產生代價,現在每個節點可以變更狀態,但變更狀態也有自己的代價,求最小總代價 。

題解

在滿二叉樹上暴力枚舉這個點子樹中是00多還是11多,然後遞歸下去DPDP合併。時間複雜度是O(2n2nn)O(2^n\cdot 2^n\cdot n)的。對於每一個葉子結點,到根的路徑最長只有nn,所有情況就是2n2^n,所以所有葉子節點加起來的時間複雜度是2n2n2^n\cdot 2^n,由於還要枚舉這條路徑上的點算代價,所以就再乘個nn

費用的計算方法就是:
對於點對(i,j)(i,j)的貢獻,如果LCALCAii異色,費用就加上一個fijf_{ij},如果LCALCAjj異色,就再加上一個fijf_{ij}。可以發現這樣算出來的答案恰好符合題意。LCALCA的顏色就表示子樹下面11多還是00多,這個是暴力枚舉的。

具體實現看代碼。

CODE

dp[i][j]dp[i][j]表示在ii的子樹裏選了jj11的最小費用。

#pragma GCC optimize (3)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 2060;
const LL INF = 1ll<<40;
int f[MAXN][2], n, all, a[MAXN];
LL dp[MAXN][MAXN], cst[MAXN][12];
bool clr[12];
void dfs(int x, int dep) {
	for(int i = 0; i <= all; ++i) dp[x][i] = INF;
	if(dep == n) {
		dp[x][0] = f[x-all][0];
		dp[x][1] = f[x-all][1];
		for(int i = 0; i < n; ++i) dp[x][clr[i]^1] += cst[x-all][i];
		return;
	}
	int half = 1<<(n-dep-1);
	clr[dep] = 0; dfs(x<<1, dep+1), dfs(x<<1|1, dep+1);
	for(int i = 0; i <= half; ++i)
		for(int j = 0; i+j <= half; ++j)
			dp[x][i+j] = min(dp[x][i+j], dp[x<<1][i] + dp[x<<1|1][j]);
	
	clr[dep] = 1; dfs(x<<1, dep+1), dfs(x<<1|1, dep+1);
	for(int i = 0; i <= half; ++i)
		for(int j = half+1-i; j <= half; ++j)
			dp[x][i+j] = min(dp[x][i+j], dp[x<<1][i] + dp[x<<1|1][j]);
}
int lca_dep(int u, int v) {
	for(int i = n-1; i >= 0; --i) if((u>>i) != (v>>i)) return n-i-1;
}
int main () {
	scanf("%d", &n); all = 1<<n;
	for(int i = 0; i < all; ++i) scanf("%d", &a[i]);
	for(int i = 0; i < all; ++i) f[i][a[i]] = 0, scanf("%d", &f[i][a[i]^1]);
	for(int i = 0; i < all; ++i)
		for(int j = i+1; j < all; ++j) {
			int k = lca_dep(i, j), v; scanf("%d", &v);
			cst[i][k] += v, cst[j][k] += v;
		}
	dfs(1, 0);
	printf("%lld\n", *min_element(dp[1], dp[1] + all + 1));
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章