1. 程式人生 > 其它 >動態規劃(六):數位型DP

動態規劃(六):數位型DP

動態規劃:數位統計DP(計數問題)

數位統計DP

AcWing 338. 計數問題

給定兩個整數 \(a\)\(b\),求 \(a\)\(b\) 之間的所有數字中 \(0 \sim 9\) 的出現次數。
例如,\(a=1024,b=1032\),則 \(a\)\(b\) 之間共有 \(9\) 個數如下:
1024 1025 1026 1027 1028 1029 1030 1031 1032
其中‘0’出現10次,‘1’出現10次,‘2’出現7次,‘3’出現3次等等…

輸入格式
輸入包含多組測試資料。 每組測試資料佔一行,包含兩個整數 \(a\)\(b\)。 當讀入一行為 \(0\) 時,表示輸入終止,且該行不作處理。

輸出格式
每組資料輸出一個結果,每個結果佔一行。 每個結果包含十個用空格隔開的數字,第一個數字表示‘0’出現的次數,第二個數字表示‘1’出現的次數,以此類推。

資料範圍
\(0 < a,b < 100000000\)

輸入樣例:

1 10
44 497
346 542
1199 1748
1496 1403
1004 503
1714 190
1317 854
1976 494
1001 1960
0 0

輸出樣例:

1 2 1 1 1 1 1 1 1 1
85 185 185 185 190 96 96 96 95 93
40 40 40 93 136 82 40 40 40 40
115 666 215 215 214 205 205 154 105 106
16 113 19 20 114 20 20 19 19 16
107 105 100 101 101 197 200 200 200 200
413 1133 503 503 503 502 502 417 402 412
196 512 186 104 87 93 97 97 142 196
398 1375 398 398 405 499 499 495 488 471
294 1256 296 296 296 296 287 286 286 247

Code:

#include <iostream>
#include <algorithm>
using namespace std;

int get(vector<int> num, int l, int r) 
{
    int res = 0;
    for(int i = l; i >= r; i --)
        res = res * 10 + num[i];
    return res;
}

int power10(int x) //求出10的x次方
{
    int res = 1;  
    while(x --)  res *= 10;
    return res;
}

int count(int n, int x)
{
    if(!n)  return 0;
    
    vector<int> num; //先計入的是數位低的數字
    while(n)
    {
        num.push_back(n % 10);
        n /= 10;
    }
    
    n = num.size();
    int res = 0;
    for(int i = n - 1 - !x; i >= 0; i --)
    {//從最高位統計x在第i位出現的次數,!x:當x=0時最高位不能為0,從第二位開始列舉
        if(i < n - 1)
        {
            res += get(num, n - 1, i + 1) * power10(i);
            if(!x)  res -= power10(i); // 當x=0時,從001開始列舉
        }
        if(num[i] == x)  res += get(num, i - 1, 0) + 1;
        else if(num[i] > x)  res += power10(i);
    }
    return res;
}

int main()
{
    int a, b;
    while(cin >> a >> b, a || b)
    {
        if(a > b)  swap(a, b);
        for(int i = 0; i < 10; i ++)
            cout << count(b, i) - count(a - 1, i) << ' ';
        cout << endl;
    }
    return 0;
}

樹形DP

AcWing 285.沒有上司的舞會

Ural大學有N名職員,編號為1~N。 他們的關係就像一棵以校長為根的樹,父節點就是子節點的直接上司。 每個職員有一個快樂指數,用整數 Hi 給出,其中 1≤i≤N。 現在要召開一場週年慶宴會,不過,沒有職員願意和直接上司一起參會。 在滿足這個條件的前提下,主辦方希望邀請一部分職員參會,使得所有參會職員的快樂指數總和最大,求這個最大值。

輸入格式 第一行一個整數N。 接下來N行,第 i 行表示 i 號職員的快樂指數Hi。 接下來N-1行,每行輸入一對整數L, K,表示K是L的直接上司。

輸出格式 輸出最大的快樂指數。

資料範圍 1≤N≤6000, −128≤Hi≤127 輸入樣例: 7 1 1 1 1 1 1 1 1 3 2 3 6 4 7 4 4 5 3 5 輸出樣例: 5

Code: 樹根和子樹的根存在上下級的關係,問題可大致分為: ①選擇樹根u,則所有子樹不可選根 f[u][1] ②不選擇樹根u,則所有子樹可選根,也可不選根 f[u][0]. 初始狀態:f[k][1] = happy[k], f[k][0] = 0(k為葉子結點) 狀態轉移:f[u][1] = ∑f[si][0], f[u][0] = ∑max(f[si][0], f[si][1]).

#include &lt;iostream&gt;
#include &lt;cstdio&gt;
#include &lt;algorithm&gt;
#include &lt;cstring&gt;
using namespace std;

const int N = 6010;
int n, h[N], e[N], ne[N], idx;
int happy[N], f[N][2];
bool has_fa[N];

void add(int a, int b)
{<!-- -->
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

void dfs(int u)
{<!-- -->
    f[u][1] = happy[u];
    
    for(int i = h[u]; i != -1; i = ne[i])
    {<!-- -->
        int j = e[i];
        dfs(j);
        f[u][1] += f[j][0];
        f[u][0] += max(f[j][0], f[j][1]);
    }
}
int main()
{<!-- -->
    scanf("%d", &amp;n);
    for(int i = 1; i &lt;= n; i ++) scanf("%d", &amp;happy[i]);
    
    memset(h, -1, sizeof h);
    for(int i = 0; i &lt; n - 1; i ++)
    {<!-- -->
        int a, b;
        scanf("%d%d", &amp;a, &amp;b);
        add(b, a);
        has_fa[a] = true; 
    }
    
    int root = 1;
    while(has_fa[root]) root ++;
    
    dfs(root);
    printf("%d\n", max(f[root][0], f[root][1]));
    return 0;
}