bzoj1495 [NOI2006] 网络收费 树形DP

来源:互联网 发布:淘宝口令在什么位置 编辑:程序博客网 时间:2024/06/10 23:33

ps:bzoj上的题目是不完整的欸。
你应该同时去bzoj和codevs上看这道题,结合一下基本就可以懂了。

简单描述:

一棵有2n(m)个节点的满二叉树,每个节点有一个颜色A或B,对于树上一个非叶子节点x,如果以x为根的子树上A颜色的节点的数量多,就让x的属性为A付费,否则为B付费。对于任意两点i,j,有一个流量Fij,它们的最近公共祖先为x,如果x与i的属性相同,就要付一倍流量,如果x与j属性相同,也要付一倍流量(所以最多的可以付两倍流量)。但是节点可以付费更改自己的颜色,求如何让所有节点付的流量总和最小。(n<=10)

(你看这个题面不就好了很多嘛)(然而还是看了题解因为不看题解看不懂题啊)
现在我们就可以方便得处理出一个节点祖先和孩子的状态了。定义costi,k为i到其第k个祖先的费用,找出i,j的LCA,让costi,kcostj,k都加上流量Fi,j
然后我们用记忆化搜索,dp(i,j,k,h)
i:表示节点
j:表示在此节点分配的颜色为A的节点数量
k:表示此节点父亲的属性(状压思想)
h:表示层数(为什么记录层数呢?因为根据层数我们可以很方便得求出它的孩子数量和祖先的状态)
开始普通背包,处理叶子节点的边界(及叶子节点到其所有祖先的花费)。
(本来到这里是经过了推论和简化的,省掉它吧)
Code真的不是很容易写啊啊啊啊啊啊啊啊

#include<algorithm>#include<cstring>#include<cstdio>using namespace std;const int oo=~0U>>1;const int N=1000+100;struct Node {    int v[N<<1],bs;    int& operator () (int i,int j) {return v[i*bs+j];};} f[N<<1];int n,m,ans=oo,c[N],type[N],fa[N][N],cost[N][N],flow[N][N];template <class T> void read(T &x) {    x=0;int f=1;char ch=getchar();    for(;ch<'0'||ch>'9';) {if(ch=='-') f=-1;ch=getchar();}    for(;ch>='0'&&ch<='9';) x=x*10+ch-'0',ch=getchar();    x*=f;}template <class T> void write(T x) {    int num=0;char ch[20];    if(x<0) putchar('-'),x=-x;    do ch[++num]=x%10+'0',x/=10; while(x);    for(;num;) putchar(ch[num--]);}int clac(int x,int j,int k) {    int i,ca=0,cb=0;    for(i=1;i<=n;i++) {        if(k&1) ca+=cost[i][x]; else cb+=cost[i][x];        k>>=1;    }    if(j) return ca+(type[x]?0:c[x]);    return cb+(type[x]?c[x]:0);}int dp(int i,int j,int k,int h) {    int l,ls,tk,now,ret=f[i](j,k);    if(ret) return ret;    if(h) {        ret=oo,now=j<((1<<h)-j);        for(tk=(k<<1)+now,ls=1<<h-1,l=(j-ls<0?0:j-ls);l<=ls&&l<=j;l++)            ret=min(ret,dp(i<<1,l,tk,h-1)+dp(i<<1|1,j-l,tk,h-1));    } else ret=clac(i-m+1,j,k);    f[i](j,k)=ret;    return ret;}int main() {    int i,j,a,k;    for(read(n),m=1<<n,i=1;i<=m;i++) read(a),type[i]=a==0;    for(i=1;i<=m;i++) read(c[i]);    for(i=1;i<=m;i++)        for(j=i+1;j<=m;j++) read(a),flow[i][j]=flow[j][i]=a;    for(i=1;i<=m;i++) for(k=i+m-1,j=1;j<=n;j++) k>>=1,fa[j][i]=k;    for(i=1;i<=n+1;i++) for(j=1<<i-1;j<=(1<<i)-1;j++) f[j].bs=1<<i-1;    for(i=1;i<=m;i++) for(j=i+1;j<=m;j++) for(k=1;k<=n;k++)        if(fa[k][i]==fa[k][j])            {cost[k][i]+=flow[i][j],cost[k][j]+=flow[i][j];break;}    for(i=0;i<=m;i++) ans=min(ans,dp(1,i,0,n));    write(ans);putchar('\n');    return 0;}
原创粉丝点击