现在需要将树上所有点都涂上颜色,问最少需要多少费用?
0<=Ax,Bx<=10000
状态:
f[i][0]: 以i为根的子树,涂上颜色的最小代价。其中i涂A颜色,与i连通的同色块(在子树内的)中,所有点都是以半价涂色的(即没有“涂色起点”)。
f[i][1]: 以i为根的子树,涂上颜色的最小代价。其中,i涂A颜色,与i连通的同色块中,存在一个节点是以全部代价涂色的(即有“涂色起点”)。
g[i][0]: 以i为根的子树,涂上颜色的最小代价。其中i涂B颜色,与i连通的同色块中,所有点都是以半价涂色的。
g[i][1]: 以i为根的子树,涂上颜色的最小代价。其中i涂B颜色,与i连通的同色块中,存在一个节点是以全部代价涂色的。
f[i][0]= Sum{ min( f[ j][0] , g[ j][1] ) } j为i的所有儿子节点
1.为什么不讨论f[j][1]呢?
因为f[i][1]表示i涂颜色A,且子树中与之相连的同色连通块中都没有全价涂A色的点,所以只能讨论f[j][0];
2.为什么不讨论g[j][0]呢?
因为g[j][0]表示j为根的子树中,与j同色的连通块全是半价的B颜色点。而j的父亲是涂的A颜色,与j连通的同色块中又没有全价的B色节点,那该同色连通块的颜色是从何点开始涂起的呢?其中必须有一个点事涂的全价B色,与g[j][0]矛盾,所以不用讨论g[j][0]。
f[i][0]: 以i为根的子树,涂上颜色的最小代价。其中i涂A颜色,与其连通的同色块(在子树内的)中,所有点都是以半价涂色的(即没有“涂色起点”)。
f[i][1]: 以i为根的子树,涂上颜色的最小代价。其中,i涂A颜色,与其连通的同色块中,存在一个节点是以全部代价涂色的(即有“涂色起点”)。
g[i][0],g[i][1]表示i涂B颜色,定义与f[ ][ ]类似
f[i][0]= Sum{ min( f[j][0] , g[j][1] ) } j为i的所有儿子节点
f[i][1]= min{
f[i][0]+ A[i]/2 //i涂全价A色
f[i][0]+ ChaJia //i的一个同色连通子树中存在节点涂全价A色
}
ChaJia=min{ f[j][1]-min(f[j][0] , g[j][1] ) } j为i的所有儿子节点
说明:
f[i][1]表示i为根的字数中,i涂A色,且与i同色的连通块中,有一个点是全价A的最优方案。 我们可以对i涂全价A,也可以使与A同色连通的某棵子树中存在全价A的点。
而f[i][0]表示i为根的子树中,i涂A色,且与i同色的连通块全是半价A的最优方案。求f[i][1]只需在f[i][0]上做调整
如果我们直接将i涂成全价的A,即是f[i][0]+A[i]/2
如果我们不把i涂成全价的A,而是选一棵与i同色连通的且存在全价A的子树替换掉原来的子树,即是f[i][0]+ChaJia 这棵子树原来加入f[i][0]的状态是min(g[j][1],f[j][0]),而新替换进的状态是f[j][1],两者之差即为替换产生的差价
最后注意,由于有除法所以有精度差,因此出数据的时候一定要非常,非常注意
#include
#include
using namespace std;
const int inf=1e9;
int n,a[205],b[205];
int f[205][5],g[205][5];
int NEXT[205],END[205],LAST[205],cnt;
void insert(int a,int b){
END[++cnt]=b;
NEXT[cnt]=LAST[a];
LAST[a]=cnt;
}
void dp(int p,int fa){
int cj1=inf,cj2=inf,temp,i,j;
bool leaf=true;
for(i=LAST[p],j=END[i];i;i=NEXT[i],j=END[i]){
if(j==fa)continue;
dp(j,p);
leaf=false;
temp=min(f[j][0],g[j][1]),f[p][0]+=temp,cj1=min(cj1,f[j][1]-temp);
temp=min(g[j][0],f[j][1]),g[p][0]+=temp,cj2=min(cj2,g[j][1]-temp);
}
if(leaf){
f[p][0]=a[p]/2,f[p][1]=a[p];
g[p][0]=b[p]/2,g[p][1]=b[p];
}
else {
f[p][0]+=a[p]/2,f[p][1]=f[p][0]+min(a[p]/2,cj1);
g[p][0]+=b[p]/2,g[p][1]=g[p][0]+min(b[p]/2,cj2);
}
}
int main(){
scanf("%d",&n);
int i,x,y,j;
for(i=1;i<=n;i++)
scanf("%d",&a[i]);
for(i=1;i<=n;i++)
scanf("%d",&b[i]);
for(i=1;i
你可能感兴趣的:(动态规划,树)