这篇博文写了一些动态规划的实践问题
树形dp,复杂状态的dp
树形dp问题
dfs解决树形dp
UVALive4472
dp(u)=i∑numdp(vi)num=100sizeu⋅T−1+1 ans=dp(0)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| const int maxn = 1e5 + 10; const int inf = 0x3f3f3f3f; int N, T;
vector<int> son[maxn];
int dp(int u) { if(son[u].empty()) return 1;
vector<int> vec; for(auto v : son[u]) { vec.push_back(dp(v)); } sort(vec.begin(), vec.end());
int num = (son[u].size() * T - 1) / 100 + 1;
int ans = 0; for(int i = 0; i < num; i++) ans += vec[i]; return ans; }
void init() { _for(i, 0, maxn) son[i].clear(); }
int main() { freopen("input.txt", "r", stdin); while (scanf("%d%d", &N, &T) == 2 && N) { init();
_rep(i, 1, N) { int x; scanf("%d", &x); son[x].push_back(i); }
// then dp int ans = dp(0); printf("%d\n", ans); } }
|
树的最大独立集问题
HDU2412
本例中加判唯一性
{d(u,1)=∑v∈son(u)d(v,0)f(u,1)={⋂v∈son(u)f(v,0)==1}
⎩⎪⎪⎨⎪⎪⎧d(u,0)=maxv∈son(u){d(v,1),d(v,0)}f(u,0)=0f(u,0)=0∀v∈son(u),d(v,0)=d(v,1)∀v∈son(u),k∈{0,1},d(v,k)>d(v,k⊕1),f(v,k)=0
st:d(maxn,2)←0,f(maxn,2)←0
ed:max(dp(0,1),dp(0,0))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| const int maxn = 200 + 10; map<string, int> mp; int tot = 0; int n; vector<int> son[maxn];
int ID(const string& str) { if(!mp.count(str)) mp[str] = tot++; return mp[str]; }
int d[maxn][2]; int f[maxn][2];
void initdp() { Set(d, 0); Set(f, 0); }
int dp(int u, int k) { d[u][k] = k; f[u][k] = 1; for(auto v : son[u]) { if(k == 1) { d[u][k] += dp(v, 0); f[u][k] &= f[v][0]; } else { d[u][k] += max(dp(v, 0), dp(v, 1)); if(d[v][0] == d[v][1]) f[u][k] = 0; else if(d[v][0] > d[v][1] && f[v][0] == 0) f[u][k] = 0; else if(d[v][1] > d[v][0] && f[v][1] == 0) f[u][k] = 0; } }
return d[u][k]; }
void init() { mp.clear(); tot = 0;
_for(i, 0, maxn) son[i].clear(); }
int main() { freopen("input.txt", "r", stdin); string s, s2; while (cin >> n >> s) { init();
ID(s); _for(i, 1, n) { cin >> s2 >> s; son[ID(s)].push_back(ID(s2)); }
// then dp initdp(); int ans = max(dp(0, 1), dp(0, 0)); printf("%d ", ans);
bool flag = true; if(d[0][0] == d[0][1]) flag = false; else if(d[0][1] > d[0][0] && f[0][1] == 0) flag = false; else if(d[0][0] > d[0][1] && f[0][0] == 0) flag = false;
flag == 0 ? printf("No\n") : printf("Yes\n"); } }
|
树形dp节点状态
LA3685
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| const int maxn = 1e5 + 10; const int inf = 0x3f3f3f3f; int n; vector<int> G[maxn];
inline void add(int u, int v) { G[u].push_back(v); }
// == dp == int d[maxn][3];
void initdp() { memset(d, -1, sizeof(d));
vector<int> ed; _rep(i, 1, n) if(G[i].size() == 0) ed.push_back(i);
for(auto x : ed) { d[x][0] = 1; d[x][1] = 0; d[x][2] = inf; } }
int dp(int u, int pa, int k) { if(d[u][k] != -1) { return d[u][k]; }
d[u][0] = 1; d[u][1] = 0;
for(auto v : G[u]) { if(v == pa) continue;
d[u][0] += min(dp(v, u, 0), dp(v, u, 1)); d[u][1] += dp(v, u, 2);
if (d[u][0] > inf) d[u][0] = inf; if (d[u][1] > inf) d[u][1] = inf;
}
d[u][2] = inf; for(auto v : G[u]) { if(v == pa) continue;
d[u][2] = min(d[u][2], d[u][1] - dp(v, u, 2) + dp(v, u, 0)); }
return d[u][k]; }
// == dp finished ==
void init() { _for(i, 0, maxn) G[i].clear(); }
int main() { freopen("input.txt", "r", stdin); while (scanf("%d", &n) == 1) { init();
// get data _for(i, 0, n - 1) { int u, v; scanf("%d%d", &u, &v); add(u, v); add(v, u); } // get data finished
initdp(); int ans = min(dp(1, 0, 0), dp(1, 0, 2)); printf("%d\n", ans); scanf("%d", &n); } }
|
算法设计策略
中位数和差分序列
UVA11300
algorithm
本例子的问题是,一个序列,通过平均分配
使得这个序列最后的值都相等,问 ∑(Δ) 最小值是多少
M=tot/n 表示最后的值
原序列是 Ai, 无论如何变化,最后的方案都可以看成是
相邻两项 “转手”
A1↔A2,A1 拿出去 x1 个元素,从 A2 处获得 x2 个元素
x1=C0=0
A1−x1+x2=M⇒x2−x1=M−A1=C1
A2−x2+x3=M⇒x3−x2=M−A2=C2
⋯⋯
xi−xi−1=M−Ai−1=Ci−1
Ci 构成了差分序列,根据差分序列的前缀和等于原序列
xi=∑j=1i−1Cj
可以根据 C 的前缀和直接得到
[x1,x2,⋯,xn]
这样问题就转换为
∀i,j,i=j,∑∣xi−xj∣ 最小
这个问题可以用中位数模型,排序后,mid=x[n/2]
ans=∑i=1n∣xi−mid∣
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const int maxn = 1e6 + 10; ll C[maxn], M = 0; ll A[maxn]; int n;
void init() { memset(C, 0, sizeof(C)); }
int main() { //freopen("input.txt", "r", stdin); while (scanf("%d", &n) == 1) { init(); ll tot = 0;
_rep(i, 1, n) { scanf("%lld", &A[i]); tot += A[i]; }
M = tot / n; _for(i, 1, n) C[i] = C[i - 1] + 1ll * M - A[i];
sort(C, C + n);
ll mid = C[n>>1]; ll ans = 0; _for(i, 0, n) ans += abs(C[i] - mid); printf("%lld\n", ans); } }
|
精度运算的一些技巧
UVA1388
pi=ni⋅1=n(n+m)i(n+m) qi=n+mi⋅1=n(n+m)in Δ=∣pi−qi∣=(n+m)∣ni⋅(n+m)−i⋅nn∣
∣ni⋅(n+m)−i⋅nn∣ 求的是 ni⋅(m) 和距离它最近的整数的距离
归一化之后,floor(x+0.5) 表示 x 四舍五入之后的值
ans=∣x−floor(x+0.5)∣
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const int L = 1e4; int n, m;
int main() { //freopen("input.txt", "r", stdin); while (scanf("%d%d", &n, &m) == 2) { double ans = 0.0; _for(i, 0, n) { double x = (double)i / n * (m); ans += fabs(x - floor(x+0.5)) / (n + m); } printf("%.4lf\n", ans * L); } }
|