近期做題記錄

近期看的東西比較多也比較雜,遇到很多想做的題目,以此博客來記錄以免遺漏。

 

2019ICPC南京網絡賽D

給你一個DAG,保證只有1沒入邊,只有n沒出邊。每天做一個選擇:以等概率走向相鄰邊或者不動。第i天的花費是i。問你從1走到n的期望花費。

因爲是DAG,我們可以直接在上面做DP來求期望。要求花費,期望公式就是:

dp[u] = \sum dp[v] * \frac{1}{out[u] + 1} + \frac{1}{out[u] + 1} * dp[u] + cost;

dp[u]表示u到n的期望花費

那麼問題就變成了如何求cost,其實也好求,再用一個DP求出期望天數就好了:

dp2[u] = \sum dp2[v] * \frac{1}{out[u] + 1} + \frac{1}{out[u] + 1} * dp2[u] + 1;

dp2[u]表示u到n的期望天數

dp2[u]就是上面對應的cost,道理也很簡單。

(寫這個好像只是爲了練一下LaTex?)

Code:

vector<int> G[maxn];
int out[maxn];
db dp1[maxn], dp2[maxn];
int n, m;

void init(){
	rep(i, 1, n) dp1[i] = dp2[i] = 0, out[i] = 0, G[i].clear();
}

db dfs1(int u){
	if(dp1[u]) return dp1[u];
	if(!out[u]) return 0;
	db sum = 0;
	for(int v : G[u]){
		sum += dfs1(v);
	}
	dp1[u] = sum / out[u] + 1 + 1.0 / out[u];
	return dp1[u];
}

db dfs2(int u){
	if(dp2[u]) return dp2[u];
	if(!out[u]) return 0;
	db sum = 0;
	for(int v : G[u]){
		sum += dfs2(v);
	}
	dp2[u] = sum / out[u] + dp1[u] * (1 + 1.0 / out[u]);
	return dp2[u];
}

int main()
{
	int T; scanf("%d", &T);
	while(T--){
		scanf("%d %d", &n, &m);
		init();
		rep(i, 1, m){
			int u, v; scanf("%d %d", &u, &v);
			out[u]++;
			G[u].pb(v);
		}
		dfs1(1);
		dfs2(1);
		printf("%.2f\n", dp2[1]);
	}
	return 0;
}

也可以寫在一個dfs裏面,懶得改了。

 

IOI 2005 河流

中文就不說題意了。

很明顯的樹形DP,主要談談如果構造狀態。根據經驗,會先構造出dp[i][j],表示以i爲根的子樹中造了j個伐木廠的最小花費。

但是這樣子是轉移不出來的,因爲不知道一個節點的花費貢獻應該是多少,所以考慮再加入一維,表示該點的木頭運送到哪個點,這樣子花費就能算出來了.在寫之前算一算時間:照這樣子寫dfs裏面有4層循環,看上去複雜度有 O(n^{4}k) 接受不了,根據經驗樹形DP的複雜度往往不是表面的那樣。考慮狀態量和轉移量,其實也就是 O(n^{3}k) 的複雜度。

來寫寫轉移方程,轉移方程自然就是子樹合併啦

 

Codeforces 506D

Codeforces 193B

HDU 4405

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章