20180828 DP解题报告

保林哥的话

本套题目检验了四类DP,当然DP肯定不仅有这里的几种类型,由于时间有限,简单和稍偏的DP未列举,未考知识点请同学们一定下来练习掌握。注:BCD三题提供简易题目大意,且题目顺序不代表题目的难易梯度,做题顺序和时间请自行把握!!!

T1 HihoCoder 1798 666

Description

如果一个数字字符串(只包含0-9,可以有前导0)中出现且只出现1次666,我们就称这个字符串是好的。

例如166603660666是好的,666666123不是好的。

请你计算长度为N的数字字符串中,有多少个是好的。由于总数可能很大,你只需要输出总数模1000000007的余数。

Input

一个整数N

对于30%的数据,1 ≤ N ≤ 8

对于100%的数据,1 ≤ N ≤ 1000

Output

一个整数代表答案。

题解

dp[i][j]表示长度为i,开头有j6且串中没有666的串的数量

这道题也就是将一个问题分成许多子问题,再合并求答案

一道比较简单但很好的数位DP

时间复杂度 \(\Theta(n)\)

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <bits/stdc++.h>
const int kMod(1000000007);
int n;
long long dp[1010][3], ans;
int main(int argc, char **argv) {
scanf("%d", &n);
dp[0][0] = 1;
for (register int i(1); i <= n; ++i) {
dp[i][0] = (dp[i - 1][0] + dp[i - 1][1] + dp[i - 1][2]) * 9 % kMod;
dp[i][1] = dp[i - 1][0];
dp[i][2] = dp[i - 1][1];
}
for (register int i(1); i < n - 1; ++i) {
ans = (ans + dp[i][1] * dp[n - i][2]) % kMod;
}
printf("%lld\n", ans);
return 0;
}

T2 Hrbust 2160 Hunter

还没做出来,有时间再补

没法补了

T3 HDU 2196 Computer

题目大意

给出一棵树,求出以每个结点为开始的最长路

题解

非常经典的树形DP

首先用邻接表存图(内存限制有点小而且有多组数据不敢用vector)

默认以1为根结点,找到以每个结点在以自身为根的子树可以走过最长路径的长度(DFS)

再找每个结点向根走的最长路(DFS)

时间复杂度 \(\Theta(n)\)

代码

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
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct Edge {
int v, w, next;
} edge[20010];
int cnt, head[10010];
inline void AddEdge(const int &u, const int &v, const int &w) {
edge[cnt].v = v, edge[cnt].w = w;
edge[cnt].next = head[u], head[u] = cnt++;
}
long long dp[3][10010];
int destination[10010];
inline void FirstDfs(const int &u, const int &father) {
register long long maximum(0), second_maximum(0);
for (register int i(head[u]); i != -1; i = edge[i].next){
register int v(edge[i].v);
if (v == father) continue;
FirstDfs(v, u);
if (maximum <= dp[0][v] + edge[i].w) {
second_maximum = maximum,
maximum = dp[0][v] + edge[i].w,
destination[u] = v;
}
else if(second_maximum < dp[0][v] + edge[i].w) {
second_maximum = dp[0][v] + edge[i].w;
}
else if(second_maximum < dp[1][v] + edge[i].w) {
second_maximum = dp[1][v] + edge[i].w;
}
}
dp[0][u] = maximum, dp[1][u] = second_maximum;
}
inline void SecondDfs(const int &u,const int &father) {
for (register int i(head[u]); i != -1; i = edge[i].next) {
register int v(edge[i].v);
if (v == father) continue;
if(destination[u] == v) {
dp[2][v] = max(dp[1][u] + edge[i].w, dp[2][u] + edge[i].w);
}
else dp[2][v] = max(dp[0][u] + edge[i].w, dp[2][u] + edge[i].w);
SecondDfs(v, u);
}
}
int n, a, b;
int main(int argc, char **argv) {
while (~scanf("%d", &n)) {
cnt = 0;
memset(head, -1, sizeof(head));
memset(dp, 0, sizeof(dp));
for (register int i(2); i <= n; ++i) {
scanf("%d %d", &a, &b);
AddEdge(i, a, b), AddEdge(a, i, b);
}
FirstDfs(1, 1);
SecondDfs(1, 1);
for (register int i(1); i <= n; ++i) {
printf("%lld\n", max(dp[0][i], dp[2][i]));
}
}
return 0;
}

T4 POJ 3071 Football

题目大意

\({2}^{n}\)个足球队,定义p[i,j]i队打败j队的概率,所有p[i,i]0,且1-p[i,j]=p[j,i]

求夺冠几率最高的球队的编号 ## 题解 概率DP,难度不大

dp[i][j]i队赢j场比赛的概率,然后就简单了

记得要先枚举比赛

时间复杂度 \(\Theta(n \cdot {2}^{2n})\) ## 代码

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
#include <cstdio>
#include <iostream>
#include <cstring>
int n, m;
double p[256][256];
int winner;
double winner_probability, dp[256][16];
int main(int argc, char **argv) {
while (std::cin >> n) {
if (n == -1) return 0;
m = 1 << n;
memset(dp, 0, sizeof(dp));
for (register int i(1); i <= m; ++i) {
dp[i][0] = 1.0;
for (register int j(1); j <= m; ++j) {
scanf("%lf", &p[i][j]);
}
}
for (register int j(1); j <= n; ++j) {
for (register int i(1); i <= m; ++i) {
for (register int k(1); k <= m; ++k) {
if ((((i - 1) >> (j - 1)) ^ 1) == ((k - 1) >> (j - 1))) {
dp[i][j] += dp[i][j - 1] * dp[k][j - 1] * p[i][k];
}
}
}
}
winner_probability = -1, winner = 0;
for (register int i(1); i <= m; ++i){
if (dp[i][n] > winner_probability) {
winner_probability = dp[i][n];
winner = i;
}
}
printf("%d\n", winner);
}
return 0;
}