树形dp

来源:互联网 发布:2007版excel数据有效性 编辑:程序博客网 时间:2024/06/10 07:06

没时间写先记下思路

f(x)
g(x)
分别记录父亲节点选和不选的情况

• 最大独立集
• 最大匹配
• 最小覆盖
• 最小支配
• *带权的上述问题模型
据说• 最大独立集和• 最大匹配互为补集
最大独立集与 最小覆盖 相同
BZOJ 1040
• 给定环套树,求带权最大独立集(我抠不出来环的放弃了)

IOI 2005 river
• 给定 n 个点的有根树
• 每条边有权,每个点有权值 w
• 你要在 k 个点上建立伐木场
• 对于每个没有建伐木场的点 x,令与它最近的、有伐⽊木场的
点为 y,你需要支付 dis(x,y) * w[x] 的代价
• 选择合适的位置建伐木场,最小化总代价
• n <= 100

首先先来分析一下标程

#include<cstdio>#include<iostream>#include<cmath>#include<cstring>#include<algorithm>#define maxn 105#define inf 1061109567using namespace std;char ch;int n,k,a,b,c,tot,w[maxn],now[maxn],son[maxn],pre[maxn],val[maxn];int dist[maxn],f[maxn][maxn][maxn],tmp[maxn][maxn][maxn];bool ok,bo[maxn][maxn];void read(int &x){    for (ok=0,ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') ok=1;    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());    if (ok) x=-x;}void put(int a,int b,int c){pre[++tot]=now[a],now[a]=tot,son[tot]=b,val[tot]=c;}int turn(int x){if (x==inf) return 0;return x;}void dfs(int u,int last){    if (bo[u][last]) return;    bo[u][last]=1;    for (int p=now[u],v=son[p];p;p=pre[p],v=son[p]){        dist[v]=dist[u]+val[p];        dfs(v,last),dfs(v,v);        for (int i=0;i<=k;i++){            f[u][last][i]=inf;            for (int j=0;j<=i;j++){                if (i>j) f[u][last][i]=min(f[u][last][i],tmp[u][last][i-j-1]+f[v][v][j]);                f[u][last][i]=min(f[u][last][i],tmp[u][last][i-j]+f[v][last][j]+w[v]*(dist[v]-dist[last]));            }        }        memcpy(tmp[u][last],f[u][last],sizeof(tmp[u][last]));    }}int main(){    read(n),read(k);    for (int i=1;i<=n;i++) read(w[i]),read(b),read(c),put(b,i,c);    dfs(0,0);    printf("%d\n",f[0][0][k]);    return 0;}

看起来csdn的页面不够宽(滑稽)

这种题还是以后再看。

月考彻底崩盘后的夜晚。
和Visit_world畅聊一晚惊觉自己好弱
于是抄了一发黄学长的代码
可能理解不是很深刻,但是有了一个初步的理解
主要思路
建树
状态:基础部件-》直接有几个来几个,类似背包问题;
高级部件-》用基础部件转移;
由于形似背包且是树形dp
我们不妨f[i][j][k]表示第I个物品用j件合成子树花费为k的最大力量值
设P[x],L[x],M[x],表示物品x的能量,购买上限与价格

L[x]=min(L[x],m/M[x])

高级装备的M和L随便dp一下

再用f[i][j][k]表示第i个物品,有j件用于上层的合成,花费金钱是k所能获得的最大力量

对于以x为根的子树,枚举合成 l 个 x 物品,然后再用其余的钱买一些 x 子树内的装备不用于合成

枚举合成物品数量 l ,用g[i][j]表示x的前i个儿子的子树,花费j的钱,所能获得的最大力量

g[tot][j]=max{g[tot-1][j-k]+f[e[i].to][l*e[i].v][k]}

//e[i].to是儿子结点,e[i].v是所需数量,就是从j中拿出k的钱在e[i].to的子树内购买

最后再枚举合成的 l 个 x 物品中有 j 个是直接用于增加力量,剩余用于合成的

f[x][j][k]=max{g[tot][k]+P[x]*(l-j)}
最后我们暴力记录结果

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<cstdlib>#include<cmath>using namespace std;int n,m,cnt,ans,tot;const int inf=1000000000;#define ll long longint p[55],l[55],M[55];int g[55][2005],f[55][105][2005];int h[55][2005];char ch[5];int deg[55],last[55];int read(){    int x=0,f=1;    char ch=getchar();    while(ch<'0'||ch>'9'){        if(ch=='-')        f=-1;        ch=getchar();    }    while(ch>='0'&&ch<='9') {        x=x*10+ch-'0';        ch=getchar();    }    return x*f;}struct data{    int to,next,v;}e[20005];void insert(int u,int v,int w){    e[++cnt].to=v; e[cnt].next=last[u];    last[u]=cnt;e[cnt].v=w;    deg[v]++;}void dp(int x){    if(!last[x]){        l[x]=min(l[x],m/M[x]);        for(int i=0;i<=l[x];i++)            for(int j=i;j<=l[x];j++)                f[x][i][j*M[x]]=(j-i)*p[x];            return ;    }    l[x]=inf;    for(int i=last[x];i;i=e[i].next){        dp(e[i].to);        l[x]=min(l[x],l[e[i].to]/e[i].v);        M[x]+=e[i].v*M[e[i].to];    }    l[x]=min(l[x],m/M[x]);    memset(g,-0x3f3f3f3f,sizeof(g));    g[0][0]=0;    for(int L=l[x];L>=0;L--){        int tot=0;        for(int i=last[x];i;i=e[i].next){            tot++;            for(int j=0;j<=m;j++)                for(int k=0;k<=j;k++)                    g[tot][j]=max(g[tot][j],g[tot-1][j-k]+f[e[i].to][L*e[i].v][k]);        }        for(int j=0;j<=L;j++)            for(int k=0;k<=m;k++)                f[x][j][k]=max(f[x][j][k],g[tot][k]+p[x]*(L-j));    }} int main(){    memset(f,-0x3f3f3f3f,sizeof(f));    n=read();m=read();    for(int i=1;i<=n;i++){        p[i]=read();        scanf("%s",ch);        if(ch[0]=='A'){            int x=read();            while(x--){                int v=read(),num=read();                insert(i,v,num);            }        }        else M[i]=read(),l[i]=read();    }    for(int x=1;x<=n;x++){        if(!deg[x]){            dp(x);            tot++;            for(int i=0;i<=m;i++)                for(int j=0;j<=i;j++)                    for(int k=0;k<=l[x];k++)                        h[tot][i]=max(h[tot][i],h[tot-1][j]+f[x][k][i-j]);        }    }    for(int i=0;i<=m;i++)        ans=max(ans,h[tot][i]);    printf("%d\n",ans);    return 0;}

不错的结果
月考炸了
但是发现VWdalao强的飞起
真的是一年自学无敌
我真是菜

1 0
原创粉丝点击