• P2465 [SDOI2008]山贼集团 dp


    这个题是一道树形dp+状压dp二合一,先预处理每种组合会有什么额外的费用,然后在树上dp就行了。

    题干:

    题目描述
    
    某山贼集团在绿荫村拥有强大的势力,整个绿荫村由N个连通的小村落组成,并且保证对于每两个小村落有且仅有一条简单路径相连。小村落用阿拉伯数字编号为1,2,3,4,…,n,山贼集团的总部设在编号为1的小村落中。山贼集团除了老大坐镇总部以外,其他的P个部门希望在村落的其他地方建立分部。P个分部可以在同一个小村落中建设,也可以分别建设在不同的小村落中。每个分部到总部的路径称为这个部门的管辖范围,于是这P个分部的管辖范围可能重叠,或者完全相同。在不同的村落建设不同的分部需要花费不同的费用。每个部门可能对他的管辖范围内的小村落收取保护费,但是不同的分部如果对同一小村落同时收取保护费,他们之间可能发生矛盾,从而损失一部分的利益,他们也可能相互合作,从而获取更多的利益。现在请你编写一个程序,确定P个分部的位置,使得山贼集团能够获得最大的收益。
    输入输出格式
    输入格式:
    
    输入文件第一行包含一个整数N和P,表示绿荫村小村落的数量以及山贼集团的部门数量。
    
    接下来N-1行每行包含两个整数X和Y,表示编号为X的村落与编号为Y的村落之间有一条道路相连。(1<=X,Y<=N)
    
    接下来N行,每行P个正整数,第i行第j个数表示在第i个村落建设第j个部门的分部的花费Aij。
    
    然后有一个正整数T,表示下面有T行关于山贼集团的分部门相互影响的代价。(0<=T<=2p)
    
    最后有T行,每行最开始有一个数V,如果V为正,表示会获得额外的收益,如果V为负,则表示会损失一定的收益。然后有一个正整数C,表示本描述涉及的分部的数量,接下来有C个数,Xi,为分部门的编号(Xi不能相同)。表示如果C个分部Xi同时管辖某个小村落(可能同时存在其他分部也管辖这个小村落),可能获得的额外收益或者损失的收益为的|V|。T行中可能存在一些相同的Xi集合,表示同时存在几种收益或者损失。
    
    输出格式:
    
    输出文件要求第一行包含一个数Ans,表示山贼集团设置所有分部后能够获得的最大收益。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    #define duke(i,a,n) for(register int i = a;i <= n;++i)
    #define lv(i,a,n) for(register int i = a;i >= n;--i)
    #define clean(a) memset(a,0,sizeof(a))
    const int INF = 1 << 30;
    typedef long long ll;
    typedef double db;
    template <class T>
    void read(T &x)
    {
        char c;
        bool op = 0;
        while(c = getchar(), c < '0' || c > '9')
            if(c == '-') op = 1;
        x = c - '0';
        while(c = getchar(), c >= '0' && c <= '9')
            x = x * 10 + c - '0';
        if(op) x = -x;
    }
    template <class T>
    void write(T x)
    {
        if(x < 0) putchar('-'), x = -x;
        if(x >= 10) write(x / 10);
        putchar('0' + x % 10);
    }
    const int N = 110;
    int lst[N],len = 0;
    int dp[N][4200];
    int val[4200];
    struct node
    {
        int l,r,nxt;
    }a[N << 1];
    int n,m;
    void add(int x,int y)
    {
        a[++len].l = x;
        a[len].r = y;
        a[len].nxt = lst[x];
        lst[x] = len;
    }
    void initdp()
    {
        int cost[N][13];
        duke(i,1,n)
        {
            duke(j,0,m - 1)
            {
                read(cost[i][j]);
            }
            dp[i][0] = 0;
            for(int j = 1;j < (1 << m);++j)
            {
                int lowbit = j & (-j);
                int lowid = (log(lowbit) + 0.001) / log(2);
                dp[i][j] = dp[i][j ^ lowbit] - cost[i][lowid];
            }
        }
    }
    void calval(int x)
    {
        duke(i,1,x)
        {
            int v,cnt,s = 0;
            read(v);read(cnt);
            duke(j,1,cnt)
            {
                int mem;
                read(mem);
                s |= (1 << (mem - 1));
            }
            int maxm = (1 << m) - 1;
            val[s] += v;
            int tmp = s ^ maxm;
            for(int j = tmp;j;j = (j - 1) & tmp)
            {
                val[(s | j)] += v;
            }
        }
    }
    void dfs(int now,int fa)
    {
        for(int k = lst[now];k;k = a[k].nxt)
        {
            int y = a[k].r;
            if(y != fa)
            {
                dfs(y,now);
                for(int j = (1 << m) - 1;j;j--)
                {
                    for(int i = j;i;i = (i - 1) & j)
                    {
                        dp[now][j] = max(dp[now][j],dp[now][j ^ i] + dp[y][i]);
                    }
                }
            }
        }
        for(int i = (1 << m) - 1;i;i--)
        {
            dp[now][i] += val[i];
        }
    }
    int main()
    {
        read(n);read(m);
        duke(i,1,n - 1)
        {
            int x,y;
            read(x);read(y);
            add(x,y);
            add(y,x);
        }
        initdp();
        int t;
        read(t);
    //    cout<<t<<endl;
        calval(t);
        dfs(1,0);
        printf("%d
    ",dp[1][(1 << m) - 1]);
        return 0;
    }
  • 相关阅读:
    一道简单的数组遍历题,加上四个条件后感觉无从下手
    五分钟小知识:如何用算法高效寻找素数?
    如何高效对有序数组/链表去重?
    中级控件——文本输入——文本变化监听器
    中级控件——对话框——提醒对话框AlertDialog
    数据存储——四种存储方式——存储卡的文件操作——私有存储空间与公共存储空间
    中级控件——文本输入——焦点变更监听器
    中级控件——对话框——日期对话框DatePickerDialog
    中级控件——对话框——时间对话框TimePickerDialog
    数据存储——四种存储方式——共享参数SharedPreferences——保存路径是:/data/data/应用包名/shared_prefs/文件名.xml——实现密码登录功能——此章节是前面的内容整合
  • 原文地址:https://www.cnblogs.com/DukeLv/p/10434434.html
Copyright © 2020-2023  润新知