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))

代码

#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
", 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
", 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
", winner);
  }
  return 0;
}
原文地址:https://www.cnblogs.com/forth/p/9548279.html