UVALive-7392 - Bundles of Joy【树型DP】【深搜】【好题】

来源:互联网 发布:python 3程序开发指南 编辑:程序博客网 时间:2024/04/30 19:10

UVALive-7392 - Bundles of Joy


这里写图片描述
这里写图片描述

题目链接:7392

题目大意:给出n种蛋糕,m家店。然后是m行,每行给出每家店买的蛋糕种类以及,买下这家店所有蛋糕的价格。问,怎么能用最少的钱买到n种蛋糕?
已知,A,B两家店蛋糕的种类满足

A完全包含B (B中有的蛋糕种类A中全有)A完全不包含B (B中有的蛋糕种类,A全没有)

注意:可能存在AB两家店卖的蛋糕是一样的,这时候需要选择价格低的那家

题目思路:首先建树,如图所示,头结点为所以得蛋糕种类。

这里写图片描述

然后从底部向上树形dp即可,

如果该节点的子节点组成的数组等于该节点,则更新该节点的dp值为两者最小值否则,该节点的dp值为该节点的val值

以下是代码:

#include <iostream>#include <iomanip>#include <fstream>#include <sstream>#include <cmath>#include <cstdio>#include <cstring>#include <cctype>#include <algorithm>#include <functional>#include <numeric>#include <string>#include <set>#include <map>#include <stack>#include <vector>#include <queue>#include <deque>#include <list>using namespace std;#define INF 1000000000struct node{    int id,val;    vector <int> s;    vector <int> child;}p[10005];int dp[100005];int vis[1005][1005];map <vector<int> ,int> mp;void init(){    mp.clear();    memset(dp,0,sizeof(dp));    memset(vis,0,sizeof(vis));   //记录第i个点第j个蛋糕是否可买    for (int i = 0; i < 10004; i++)    {        p[i].id = 0;        p[i].val = 0;        p[i].child.clear();        p[i].s.clear();    }}bool cmp(node a,node b){    if (a.s.size() != b.s.size())        return a.s.size() < b.s.size();    else return a.id > b.id;}bool cmp2(node a,node b){    return a.id < b.id;}void solve(int r){    int len = p[r].child.size();    if (len == 0)   //如果没有儿子    {        dp[r] = p[r].val;        return;    }    int sumchild = 0;    vector <int> a;   //记录所有儿子的值    for (int i = 0; i < len; i++)    {        int cld = p[r].child[i];        solve(cld);  //搜儿子        a.insert(a.end(),p[cld].s.begin(),p[cld].s.end());   //将所有儿子的值合并到一个数组中。        sumchild += dp[cld];   //把儿子的权值加在一起    }    sort(a.begin(),a.end());    sort(p[r].s.begin(),p[r].s.end());    if (p[r].s == a)   //如果儿子能代替该父节点(eg:儿子:1,2,345; 父亲: 12345)    {        dp[r] = min(p[r].val,sumchild);    }    else    {        dp[r] = p[r].val;    }}int main(){    int t;    cin >> t;    while(t--)    {        int n,m;        cin >> m >> n;        init();  //初始化        p[0].id = 0;   //设置头结点        for (int i = 1; i <= m; i++)        {            p[0].s.push_back(i);   //头结点的s数组值为全部的蛋糕编号            vis[0][i] = 1;        }        p[0].val = INF;        int tmp = 0;        for (int i = 1,j = 1; j <= n; i++,j++)        {            cin >> p[i].val;            int k;            cin >> k;            p[i].s.clear();            memset(vis[i],0,sizeof(vis[i]));            while(k--)            {                int num;                cin >> num;                p[i].s.push_back(num);                vis[i][num] = 1;            }            p[i].id = i;            //去重,如果该数组出现过,则与之前进行比较,记录下花费较小值。            if (!mp[p[i].s]) mp[p[i].s] = i;            else            {                p[mp[p[i].s]].val = min(p[i].val,p[mp[p[i].s]].val);                i--;                tmp++;   //记录删去了几个点            }        }        n -= tmp;        //找第i个结点的父节点        sort(p,p + n + 1,cmp);        for (int i = 0; i < n; i++)        {            for (int j = i + 1; j <= n; j++)            {                int ok = 1;                for (int k = 0; k < p[i].s.size(); k++)  //遍历第i个店的所有蛋糕编号                {                    int no = p[i].s[k];                    if (vis[p[j].id][no] == 0)   //如果第j个店不全部包括第i个店的所有蛋糕                    {                        ok = 0;                        break;                    }                }                if (ok)                {                    p[j].child.push_back(p[i].id);   //第j这个店的儿子是x这个店,注意存的是i的id值而不是i                    break;                }            }        }        sort(p,p + n + 1,cmp2);        solve(0);        cout << dp[0] << endl;    }    return 0;}/* 100 4 3 20 2 1 2 20 2 3 4 38 4 1 2 3 4 2 3 5 1 1 10 2 1 2 4 1 2 2 2 1 1 1 5 2 2 1 1 2 2 1 1  1 1 1 4 4 10 2 1 2 5 2 3 4 10 2 3 4 38 1 1 10 5 195 2 3 7 1113 2 4 5 99 1 8 4510 2 2 3 345 10 1 2 3 4 5 6 7 8 9 10 10 5 195 4 1 3 6 7 234 6 2 4 5 8 9 10 1000 10 1 2 3 4 5 6 7 8 9 10 10 2 1 3 10 2 6 7 */
1 0