大白书DP习题

本文深入探讨了深度学习与数据科学的核心概念、技术应用与实践案例,涵盖机器学习、神经网络、自然语言处理、计算机视觉等多个方向,旨在提供一个全面的教程,帮助读者掌握关键技能并应用于实际问题解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

9月7号~

开学啦,时间有点紧张。。

暑假算是白费啦~

今天开始刷大白书DP习题。。

一天一个!

废话不多说。。开刷!


Partitioning by Palindromes

第一天~

题目传送:UVA - 11584 - Partitioning by Palindromes

分类:DP入门题。

分析:因为是考虑回文串,很容易想到用两个指针来找以当前点为中点的回文串,每次找到一个回文串就进行更新,不过要注意偶数回文和奇数回文的情况

WA了两次,没注意到每次找到回文串都要更新还有刚开始需要更新一下。。

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

char s[1005];
int dp[1005];//dp[i]表示以i为结尾的子串能够划分的最少回文串

int main() {
    int n;
    s[0] = '$';
    scanf("%d", &n);
    while(n -- ) {
        scanf("%s", s + 1);
        int len = strlen(s + 1);
        for(int i = 0; i <= len; i ++) dp[i] = i;
        for(int i = 1; i <= len; i ++) {
            int p = i - 1, q = i + 1;//左右两个指针
            int l = 1;//s[i]为中点时的回文串的长度
            dp[i] = min(dp[i], dp[p] + 1);//记得更新所有可能的情况
            while(s[i] == s[q] && q <= len) {
                l ++;
                q ++;
                dp[q - 1] = min(dp[q - 1], dp[p] + 1);//记得每走一步都要记得更新一下,不然只更新最后一步会错,比如bdbacabcb
            }
            while(p >= 0 && q <= len && s[p] == s[q]) {
                l += 2;
                p --;
                q ++;
                dp[q - 1] = min(dp[q - 1], dp[p] + 1);
            }
            dp[q - 1] = min(dp[q - 1], dp[p] + 1);
        }
        printf("%d\n", dp[len]);
    }
    return 0;
}



Salesmen

第二天~

题目传送:UVALive - 4256 - Salesmen

分类:DP入门题。

分析:数据比较小,直接考虑暴力递推之。。

此题较顺利,,1A。

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0xfffffff
using namespace std;

int n, n1, n2;

int mp[105][105];
int A[205];
int dp[205][105];//dp[i][j]表示序列中第i个数字为j时需要修改的最少的数

int main() {
    int T;
    scanf("%d", &T);
    while(T --) {
        memset(mp, 0, sizeof(mp));
        scanf("%d %d", &n1, &n2);
        int u, v;
        for(int i = 0; i < n2; i ++) {
            scanf("%d %d", &u, &v);
            mp[u][v] = 1;
            mp[v][u] = 1;
        }
        for(int i = 1; i <= n1; i ++) mp[i][i] = 1;

        scanf("%d", &n);
        for(int i = 1; i <= n; i ++) {
            scanf("%d", &A[i]);
        }

        //初始化
        for(int i = 0; i < 205; i ++) {
            for(int j = 0; j < 105; j ++) {
                dp[i][j] = INF;
            }
        }
        for(int i = 1; i <= n1; i ++) {
            if(i != A[1]) dp[1][i] = 1;
            else dp[1][i] = 0;
        }
        //DP递推
        for(int p = 2; p <= n; p ++) {//枚举序列的第几个位置
            for(int i = 1; i <= n1; i ++) {//枚举该位置的每个可能取值
                if(i != A[p]) {//此位置改变
                    for(int j = 1; j <= n1; j ++) {
                        if(mp[i][j] == 1) {
                            dp[p][i] = min(dp[p][i], dp[p-1][j] + 1);
                        }
                    }
                }
                else if(i == A[p]) {
                    for(int j = 1; j <= n1; j ++) {
                        if(mp[i][j] == 1) {
                            dp[p][i] = min(dp[p][i], dp[p-1][j]);
                        }
                    }
                }
            }
        }

        //for(int i = 1; i <= n; i ++) {
        //  for(int j = 1; j <= n1; j ++) {
        //      cout << dp[i][j] << " ";
        //  }
        //  cout << endl;
        //}

        int ans = INF;
        for(int i = 1; i <= n1; i ++) {
            ans = min(ans, dp[n][i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}



Wavio Sequence

第三天~

题目传送:UVA - 10534 - Wavio Sequence

分类:序列型DP(最长上升子序列)

分析:根据题目意思,可以知道只需要考虑每一个点作为中点时,维护往前的最长上升子序列,和往后的最长下降子序列即可,两个一前一后的子序列之间只要取较小的那个值就是此点的k+1,则更新答案ans = max(ans, 2* (k + 1) - 1)。1A。

其中最长上升子序列用了lower_bound,复杂度为O(n*logn)。

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

int n;
int dp1[10005];//dp1[i]表示从1到i的i个数中的最长上升子序列
int dp2[10005];//dp2[i]表示从i到n的n-i+1个数中的最长下降子序列
int a[10005];
int g[10005];//辅助数组

int main() {
    while(scanf("%d", &n) != EOF) {
        for(int i = 1; i <= n; i ++) {
            scanf("%d" , &a[i]);
        }

        for(int i = 1; i <= n; i ++) g[i] = INF;
        for(int i = 1; i <= n; i ++) {
            int k = lower_bound(g + 1, g + n + 1, a[i]) - g;
            g[k] = a[i];
            dp1[i] = k;
        }

        for(int i = 1; i <= n; i ++) g[i] = INF;
        for(int i = n; i >= 1; i --) {
            int k = lower_bound(g + 1, g + n + 1, a[i]) - g;
            g[k] = a[i];
            dp2[i] = k;
        }
        int ans = 0;
        for(int i = 1; i <= n; i ++) {
            int t = min(dp1[i], dp2[i]);
            ans = max(ans, t * 2 - 1);
        }
        printf("%d\n", ans);
    }
    return 0;
}



Fewest Flops

第四天~

题目传送:UVA - 11552 - Fewest Flops

分类:简单DP,重在设计状态以及状态怎么转移

分析:分成len/k个组就行了,然后定义dp[i][j]表示第i组以j结尾时的最小块数。然后递推即可,注意递推的时候要判断当前状态和前一状态是否存在这个字母,不然会出错,这里分别WA了一下。

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
#define min(a, b) (a) < (b) ? (a) : (b)
using namespace std;

int k;
char s[1005];
int d[1005][26];//d[i][0~25]代表第i个组内是否存在a~z这些字母
int cnt[1005];//cnt[i]代表第i组有多少个不同的字母
int dp[1005][26];//d[i][0~25]代表第i个组以a~z结尾时的最小块数

int main() {
    int T;
    scanf("%d", &T);
    while(T --) {
        memset(cnt, 0, sizeof(cnt));
        memset(d, 0, sizeof(d));
        scanf("%d %s", &k, s);
        int len = strlen(s);
        int zu = len / k;
        int p = 0;
        for(int i = 1; i <= zu; i ++) {
            for(int j = 0; j < k; j ++, p ++) {
                if(d[i][s[p] - 'a'] == 0) {
                    cnt[i] ++;
                    d[i][s[p] - 'a'] = 1;
                }
            }
        }

        //for(int i = 1; i <= zu; i ++) cout << cnt[i] << " "; cout << endl;

        //边界
        for(int i = 0; i <= zu; i ++) {
            for(int j = 0; j < 26; j ++) {
                dp[i][j] = INF;
            }
        }
        for(int i = 0; i < 26; i ++) {
            dp[1][i] = cnt[1];
        }

        //递推
        for(int i = 2; i <= zu; i ++) {
            for(int j = 0; j < 26; j ++) {//递推当前的26个字母
                if(d[i][j] == 1) {//判断当前是否存在j这个字母
                    for(int k = 0; k < 26; k ++) {//递推前一个状态的26个字母
                        if(d[i-1][k]) {//判断前一个状态是否存在k这个字母
                            if((d[i][k] == 1 && k != j) || (d[i][k] == 1 && k == j && cnt[i] == 1)) {
                                dp[i][j] = min(dp[i][j], dp[i-1][k] + cnt[i] - 1);
                            }
                            else dp[i][j] = min(dp[i][j], dp[i-1][k] + cnt[i]);
                        }
                    }
                }
            }
        }
        int ans = INF;
        //for(int i = 1; i <= zu; i ++) {
        //  for(int j = 0; j < 26; j ++) {
        //      cout << dp[i][j] << " ";
        //  }
        //  cout << endl;
        //}
        for(int i = 0; i < 26; i ++) {
            ans = min(ans, dp[zu][i]);
        }
        printf("%d\n", ans);
    }
    return 0;
}



Palindromic Subsequence

第五天~

题目传送:UVA - 11404 - Palindromic Subsequence

分类:序列型DP

分析:就是类似的LCS问题,如果输出长度就很简单,不过这里要输出回文串,想了半天不知怎么存,无奈搜了下题解,原来这里可以写在一个结构体里面来记录最小字典序,还要注意那个最小字典序可能不是回文串,需要奇偶分别判断一下输出

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

const int maxn = 1005;

struct node {
    int len;
    string str;
}dp[maxn][maxn];//类似LCS的dp,只不过加上了此时的最小字典序

char s1[maxn];
char s2[maxn];

int n, len;

int main() {
    while(scanf("%s", s1 + 1) != EOF) {
        len = strlen(s1 + 1);
        s2[len + 1] = '\0';

        for(int i = 1; i <= len; i ++) {
            s2[len - i + 1] = s1[i];
        }

        //初始化
        for(int i = 0; i <= len; i ++) {
            dp[0][i].len = 0;
            dp[0][i].str = "";
        }
        //printf("%s %s\n", s1 + 1, s2 + 1);

        //递推
        for(int i = 1; i <= len; i ++) {
            for(int j = 1; j <= len; j ++) {
                if(s1[i] == s2[j]) {
                    dp[i][j].len = dp[i-1][j-1].len + 1;
                    dp[i][j].str = dp[i-1][j-1].str + s1[i];
                }
                else {
                    if(dp[i-1][j].len > dp[i][j-1].len) {
                        dp[i][j].len = dp[i-1][j].len;
                        dp[i][j].str = dp[i-1][j].str;
                    }
                    else if(dp[i][j-1].len > dp[i-1][j].len) {
                        dp[i][j].len = dp[i][j-1].len;
                        dp[i][j].str = dp[i][j-1].str;
                    }
                    else {
                        dp[i][j].len = dp[i-1][j].len;
                        dp[i][j].str = min(dp[i-1][j].str, dp[i][j-1].str);
                    }
                }
            }
        }

        int ma = dp[len][len].len;
        string ans = dp[len][len].str;

        if(ma & 1) {
            for(int i = 0; i < ma / 2; i ++) {
                cout << ans[i];
            }
            for(int i = ma / 2; i >= 0; i --) {
                cout << ans[i];
            }
            cout << endl;
        }
        else {
            for(int i = 0; i < ma / 2; i ++) {
                cout << ans[i];
            }
            for(int i = ma / 2 - 1; i >= 0; i --) {
                cout << ans[i];
            }
            cout << endl;
        }

    }
    return 0;
}



Cellular Network

第六天~

有点累。。

题目传送:UVALive - 4731 - Cellular Network

分类:贪心+概率DP

分析:根据递推式可以很清楚的知道要将概率先从大到小排序,因为要尽可能小。
此外,设dp[i][j]表示前i个分成j组的最小期望值,递推即可

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

double dp[105][105];//dp[i][j]表示前i个分成j组的最小值

double u[105];
double p[105];
double qzh[105];

int n, w;

bool cmp(double a, double b) {
    return a > b;
}

int main() {
    int T;
    scanf("%d", &T);
    while(T --) {
        scanf("%d %d", &n, &w);
        double sum = 0;
        for(int i = 1; i <= n; i ++) {
            scanf("%lf", &u[i]);
            sum += u[i];
        }
        for(int i = 1; i <= n; i ++) {
            p[i] = u[i] / sum;
        }

        sort(p + 1, p + n + 1, cmp);
        //for(int i = 1; i <= n; i ++) cout << p[i] << " "; cout << endl;
        for(int i = 1; i <= n; i ++) {
            qzh[i] = qzh[i-1] + p[i];
        }

        for(int i = 1; i <= n; i ++) {//递推前i个
            dp[i][1] = i * qzh[i];
            for(int j = 2; j <= w && j <= i; j ++) {//分成j组
                dp[i][j] = INF;
                for(int k = j - 1; k < i; k ++) {//枚举前一状态k个分为j-1组时的情况
                    dp[i][j] = min(dp[i][j], dp[k][j-1] + i * (qzh[i] - qzh[k]));
                }
            }
        }
        printf("%.4lf\n", dp[n][w]);
    }
    return 0;
}



Mega Man’s Mission

第七天~

题目传送:UVA - 11795 - Mega Man’s Mission

分类:状态压缩DP

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

const int maxn = (1 << 16) + 5;

int n;

LL dp[maxn];//dp[i]表示状态为i时的顺序总数
int kill[maxn];//kill[i]表示状态为i时可以消灭的机器人

int wuqi[35];//武器属性

int main() {
    int T;
    scanf("%d", &T);
    for(int cas = 1; cas <= T; cas ++) {
        memset(wuqi, 0, sizeof(wuqi));

        char s[35];
        scanf("%d", &n);
        for(int i = 0; i <= n; i ++) {
            scanf("%s", s);
            wuqi[i] = 0;
            for(int j = 0; s[j]; j ++) {
                if(s[j] == '1') {
                    wuqi[i] |= (1 << j);
                }
            }
        }

        int tot = (1 << n) - 1;
        kill[0] = wuqi[0];
        for(int s = 1; s <= tot; s ++) {
            kill[s] = wuqi[0];
            for(int i = 1; i <= n; i ++) {
                if(s & (1 << (i - 1))) kill[s] |= wuqi[i];
            }
        }

        memset(dp, 0, sizeof(dp));
        dp[0] = 1;//一个机器人都不杀的方案为1
        for(int i = 1; i <= tot; i ++) {//枚举全集
            for(int j = 1; j <= n; j ++) {//枚举当前集合去掉第j个机器人的子集
                if(i & (1 << (j -1))) {
                    int zi = i ^ (1 << (j - 1));
                    if(kill[zi] & (1 << (j - 1))) {
                        dp[i] += dp[zi];
                    }
                }
            }
        }
        //for(int i = 0; i <= tot; i ++) {
        //  printf("%d %d %d\n", i, dp[i], wuqi[i]);
        //}
        printf("Case %d: %lld\n", cas, dp[tot]);
    }
    return 0;
}



Jump

第八天~

有点破事,,隔了一天才弄

题目分类:变形的Joseph问题

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

const int maxn = 500005;
int f[maxn];
int a[5];

int n, k;

int main() {
    int T;
    scanf("%d", &T);
    while(T --) {
        scanf("%d %d", &n, &k);
        for(int i = 1; i <= 3; i ++) {
            f[i] = (k - 1) % i;
            for(int j = i + 1; j <= n; j ++) f[j] = (f[j - 1] + k) % j;
            a[i] = (1 + f[n]) % n;
            if(a[i] <= 0) a[i] += n;
        }
        printf("%d %d %d\n", a[3], a[2], a[1]);
    }
    return 0;
}



Martian Mining

第九天~

题目传送:UVALive - 3530 - Martian Mining

有点累了,直接上代码吧。。

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

const int maxn = 505;
int n, m;
int A[maxn][maxn];
int B[maxn][maxn];

int dpa[maxn][maxn];//dpa[i][j]表示以i,j为右下角,1,1为左上角所围成的矩形里,在i,j这个点运输A可以得到的最大值
int dpb[maxn][maxn];//dpb[i][j]表示以i,j为右下角,1,1为左上角所围成的矩形里,在i,j这个点运输B可以得到的最大值

int sum_row[maxn][maxn];
int sum_col[maxn][maxn];

int main() {
    while(scanf("%d %d", &n, &m) != EOF) {
        if(n == 0 && m == 0) break;

        memset(dpa, 0, sizeof(dpa));
        memset(dpb, 0, sizeof(dpb));

        for(int i = 1; i <= n; i ++) {
            for(int j = 1; j <= m; j ++) {
                scanf("%d", &A[i][j]);
                sum_row[i][j] = sum_row[i][j-1] + A[i][j];
            }
        }
        for(int i = 1; i <= n; i ++) {
            for(int j = 1; j <= m; j ++) {
                scanf("%d", &B[i][j]);
                sum_col[i][j] = sum_col[i-1][j] + B[i][j];
            }
        }
        for(int i = 1; i <= n; i ++) {
            for(int j = 1; j <= m; j ++) {
                dpa[i][j] = max(dpa[i-1][j], dpb[i-1][j]) + sum_row[i][j];
                dpb[i][j] = max(dpa[i][j-1], dpb[i][j-1]) + sum_col[i][j];
            }
        }
        printf("%d\n", max(dpa[n][m], dpb[n][m]));
    }
    return 0;
}



Paths through the Hourglass

第十天~

题目传送:UVA - 10564 - Paths through the Hourglass

啊啊啊啊啊,,不行了啊,,感觉做不动了,,虽然这个还是比较水的题。。因为没有注意到有个地方要反过来,,萎了好久好久。。。

分类:类似01背包的题

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

const int maxn = 385;
int n, S;

int a[45][25];
LL dp[45][25][maxn];//dp[i][j][k]的值表示在位置i,j的时候,选了位于i,j的数时有多少路径上的数之和等于k(这里是逆推)

int main() {
    while(scanf("%d %d", &n, &S) != EOF) {
        if(n == 0 && S == 0) break;
        for(int i = 1; i <= n; i ++) {
            for(int j = 1; j <= n - i + 1; j ++) {
                scanf("%d", &a[i][j]);
            }
        }
        for(int i = n + 1; i <= 2 * n - 1; i ++) {
            for(int j = 1; j <= i - n + 1; j ++) {
                scanf("%d", &a[i][j]);
            }
        }

        if(S >= 360) {
            printf("0\n\n");
            continue;
        }
        //因为要打印路径,,所以采用逆推比较好
        memset(dp, 0, sizeof(dp));
        for(int i = 1; i <= n; i ++) {
            dp[2 * n - 1][i][a[2 * n - 1][i]] = 1;
        }
        for(int i = 2 * n - 2; i >= n; i --) {
            for(int j = 1; j <= i - n + 1; j ++) {
                for(int k = S; k >= 0; k --) {
                    dp[i][j][k + a[i][j]] += dp[i + 1][j][k] + dp[i + 1][j + 1][k];
                }
            }
        }
        for(int i = n - 1; i >= 1; i --) {
            for(int j = 1; j <= n - i + 1; j ++) {
                for(int k = S; k >= 0; k --) {
                    dp[i][j][k + a[i][j]] += dp[i + 1][j - 1][k] + dp[i + 1][j][k];
                }
            }
        }
        LL cnt = 0;
        for(int i = 1; i <= n; i ++) {
            cnt += dp[1][i][S];
        }
        cout << cnt << endl;
        if(cnt == 0) {
            cout << endl;
            continue;
        }
        int p;
        for(int i = 1; i <= n; i ++) {
            if(dp[1][i][S] >= 1) {
                p = i;
                break;
            }
        }
        printf("%d ", p - 1);
        int prev = S;//打印路径
        for(int i = 2; i <= n; i ++) {
            if(dp[i][p-1][S-a[i-1][p]]) {
                cout << 'L';
                S -= a[i-1][p];
                p = p - 1;
            }
            else {
                cout << 'R';
                S -= a[i-1][p];
            }
        }
        for(int i = n + 1; i <= 2 * n - 1; i ++) {
            if(dp[i][p][S-a[i-1][p]]) {
                cout << 'L';
                S -= a[i-1][p];
            }
            else {
                cout << 'R';
                S -= a[i-1][p];
                p = p + 1;
            }
        }
        cout << endl;
    }
    return 0;
}



Strategic game

第11天~

题目传送:UVALive - 2038 - Strategic game

题目分类:树形DP

题目分析:

基础的树形DP(最小顶点覆盖)

首先定义:

  • dp[i][0]代表以i为根节点的子树,不选根节点时所需要的最小点数。
  • dp[i][1]代表以i为根节点的子树,选根节点时所需要的最小点数。

先考虑不选根节点时,因为要覆盖所有边,所以他的子节点都要选。
再考虑选了根节点时,此时,他的子节点可选可不选,取较小的那个即可。

则有状态转移方程:

  • dp[u][0] = dp[v][1] (v为u的子节点)
  • dp[u][1] = min(dp[v][0], dp[v][1]) (v为u的子节点)

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

const int maxn = 1505;
int n;

vector<int> G[maxn];
int dp[maxn][2];//dp[i][0]表示以i为根的子树的根节点不选的情况,dp[i][1]表示以i为根的子树的根节点选了的情况

void dfs(int u, int fa) {
    dp[u][0] = 0;
    dp[u][1] = 1;
    int d = G[u].size();
    for(int i = 0; i < d; i ++) {
        int v = G[u][i];
        if(v != fa) {
            dfs(v, u);
        }
    }
    for(int i = 0; i < d; i ++) {
        int v = G[u][i];
        if(v != fa) {
            dp[u][0] += dp[v][1];
            dp[u][1] += min(dp[v][0], dp[v][1]);
        }
    }
}

int main() {
    while(scanf("%d", &n) != EOF) {
        for(int i = 0; i <= n; i ++) {
            G[i].clear();
        }
        int u, v, t;
        for(int i = 0; i < n; i ++) {
            scanf("%d:(%d)", &u, &t);
            for(int i = 1; i <= t; i ++) {
                scanf("%d", &v);
                G[u].push_back(v);
                G[v].push_back(u);
            }
        }
        memset(dp, 0, sizeof(dp));
        dfs(0, -1);
        printf("%d\n", min(dp[0][0], dp[0][1]));
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值