20180828 DP解题报告
保林哥的话
本套题目检验了四类DP,当然DP肯定不仅有这里的几种类型,由于时间有限,简单和稍偏的DP未列举,未考知识点请同学们一定下来练习掌握。注:BCD三题提供简易题目大意,且题目顺序不代表题目的难易梯度,做题顺序和时间请自行把握!!!
T1 HihoCoder 1798 666
Description
如果一个数字字符串(只包含0-9
,可以有前导0
)中出现且只出现1次666
,我们就称这个字符串是好的。
例如1666
、03660666
是好的,6666
、66
、123
不是好的。
请你计算长度为N
的数字字符串中,有多少个是好的。由于总数可能很大,你只需要输出总数模1000000007
的余数。
Input
一个整数N
。
对于30%
的数据,1 ≤ N ≤ 8
对于100%
的数据,1 ≤ N ≤ 1000
Output
一个整数代表答案。
题解
dp[i][j]
表示长度为i
,开头有j
个6
且串中没有666
的串的数量
这道题也就是将一个问题分成许多子问题,再合并求答案
一道比较简单但很好的数位DP
时间复杂度 \(\Theta(n)\)
代码
#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)\)
代码
#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})\)
代码
#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;
}