近期看的东西比较多也比较杂,遇到很多想做的题目,以此博客来记录以免遗漏。
2019ICPC南京网络赛D
给你一个DAG,保证只有1没入边,只有n没出边。每天做一个选择:以等概率走向相邻边或者不动。第i天的花费是i。问你从1走到n的期望花费。
因为是DAG,我们可以直接在上面做DP来求期望。要求花费,期望公式就是:
dp[u]表示u到n的期望花费
那么问题就变成了如何求cost,其实也好求,再用一个DP求出期望天数就好了:
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层循环,看上去复杂度有 接受不了,根据经验树形DP的复杂度往往不是表面的那样。考虑状态量和转移量,其实也就是 的复杂度,刚好可以通过这题。
来写写转移方程,转移方程自然就是子树合并啦
我们开两个数组来记录:
f[i][j][k]//i点为根的子树中,建造了k个伐木厂,有一个祖先j有伐木厂,i点建造伐木厂时的最小花费
g[i][j][k]//i点为根的子树中,建造了k个伐木厂,有一个祖先j有伐木厂,i点没有建造伐木厂时的最小花费
那么具体的转移就是:
f[u][j][k] = qmin(f[u][j][k], f[u][j][k-x] + f[v][u][x]);
g[u][j][k] = qmin(g[u][j][k], g[u][j][k-x] + g[v][j][x]);
//x为枚举的儿子的伐木厂的个数
再考虑初始化:
f[u][j][k] += f[v][u][0];
g[u][j][k] += g[v][j][0];
还要注意的一点就是在一个节点的状态跑完了回溯回去之后,我们要用的是它的贡献,但是显然分类该点有没有伐木厂贡献是不全的,所以还要合并一下f和g数组,把新的花费的贡献也加进去。
接下来就是完整代码啦~
---------------把爷整吐了,明天再写------------------
哎昨天太傻了,把u写成了i,今天改了调一调细节就过了。
int n, K;
int val[105], sum[105];
vector<PII> G[105];
int acst[105], tot = 0;
int f[105][105][55], g[105][105][55];
int sz[105];
void work(int u){
sz[u] = 1;
for(PII nxt : G[u]){
int v = nxt.fi, w = nxt.se;
sum[v] = sum[u] + w;
work(v);
sz[u] += sz[v];
}
}
void dfs(int u){
acst[++tot] = u;
int up1 = qmin(K, sz[u]);
for(PII nxt : G[u]){
int v = nxt.fi;
dfs(v);
rep(i, 1, tot){
int j = acst[i];
Rep(k, up1, 0){
f[u][j][k] += g[v][u][0];
g[u][j][k] += g[v][j][0];
int up2 = qmin(sz[v], k);
rep(x, 0, up2){
f[u][j][k] = qmin(f[u][j][k], f[u][j][k-x] + g[v][u][x]);
g[u][j][k] = qmin(g[u][j][k], g[u][j][k-x] + g[v][j][x]);
}
}
}
}
rep(i, 1, tot){
int j = acst[i];
Rep(k, up1, 0){
if(k) g[u][j][k] = qmin(g[u][j][k] + val[u] * (sum[u] - sum[j]), f[u][j][k-1]);
else g[u][j][k] += val[u] * (sum[u] - sum[j]);
}
}
tot--;
}
int main()
{
scanf("%d %d", &n, &K);
rep(i, 1, n){
int v, d; scanf("%d %d %d", val + i, &v, &d);
G[v].pb(mp(i, d));
}
work(0);
dfs(0);
printf("%d\n", g[0][0][K]);
return 0;
}