• 【BZOJ4711】小奇挖矿 树形DP


    【BZOJ4711】小奇挖矿

    Description

    【题目背景】
    小奇在喵星系使用了无限非概率驱动的采矿机,以至于在所有星球上都采出了一些矿石,现在它准备建一些矿石仓库并把矿石运到各个仓库里。
    【问题描述】
    喵星系有n个星球,标号为1到n,星球以及星球间的航线形成一棵树。所有星球间的双向航线的长度都为1。小奇要在若干个星球建矿石仓库,设立每个仓库的费用为K。对于未设立矿石仓库的星球,设其到一个仓库的距离为i,则将矿石运回的费用为Di。请你帮它决策最小化费用。

    Input

    第一行2个整数n,K。
    第二行n-1个整数,D1,D2,…Dn-1,保证Di<=Di+1。
    接下来n-1行,每行2个整数x,y,表示星球x和星球y存在双向航线。
    n<=200,0<=K,Di<=100000

    Output

    输出一行一个整数,表示最小费用。

    Sample Input

    8 10
    2 5 9 11 15 19 20
    1 4
    1 3
    1 7
    4 6
    2 8
    2 3
    3 5

    Sample Output

    38
    【样例解释】
    在1,2号星球建立仓库。

    题解:前几天做了难么多奇葩的树形DP,然而这题还是没做出来。

    n=200,那么应该是个二维或三维的状态。用f[x][y]表示x这个点运到y,并且x子树中的其它点都已经确定了运到哪,且y位置还没建仓库的最小费用。转移方法也是挺神的。

    如果我们要用f[a][b]更新f[x][y](其中a是x的儿子),那么有几种情况:

    1.b=y,由于我们先不用再y建仓库,所以直接用f[x][y]+f[a][b]更新。
    2.b在a的子树中,那么在a的位置建一个即可,所以用f[x][y]+f[a][b]+K更新。
    3.b不在a的子树中,这时,我们要么将y改成b,要么将b改成y,一定会使得答案变得更优,所以:不用更新!

    最后答案就是min{f[1][x]+K}。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    using namespace std;
    int n,K,cnt,ans;
    int f[210][210],D[210],dep[210],p[210],q[210],head[210],to[410],next[410],fa[210],dis[210][210];
    void init(int x)
    {
    	p[x]=++p[0];
    	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x])	fa[to[i]]=x,dep[to[i]]=dep[x]+1,init(to[i]);
    	q[x]=p[0];
    }
    void dfs(int x)
    {
    	f[x][0]=K;
    	int i,j,k,y;
    	for(j=1;j<=n;j++)	f[x][j]=dis[x][j];
    	for(i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x])
    	{
    		y=to[i],dfs(y);
    		for(j=1;j<=n;j++)
    		{
    			int fx=1<<30;
    			for(k=1;k<=n;k++)
    			{
    				if(j==k)	fx=min(fx,f[x][j]+f[y][k]);
    				else	if(p[k]>=p[y]&&p[k]<=q[y])	fx=min(fx,f[x][j]+f[y][k]+K);
    			}
    			f[x][j]=fx;
    		}
    	}
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    inline void add(int a,int b)
    {
    	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
    }
    int main()
    {
    	n=rd(),K=rd();
    	int i,j,a,b;
    	for(i=1;i<n;i++)	D[i]=rd();
    	memset(head,-1,sizeof(head));
    	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
    	init(1);
    	for(i=1;i<=n;i++)	for(j=i;j<=n;j++)
    	{
    		a=i,b=j;
    		if(dep[a]<dep[b])	swap(a,b);
    		while(dep[a]>dep[b])	a=fa[a];
    		while(a!=b)	a=fa[a],b=fa[b];
    		dis[i][j]=dis[j][i]=D[dep[i]+dep[j]-2*dep[a]];
    	}
    	memset(f,0x3f,sizeof(f));
    	dfs(1);
    	ans=1<<30;
    	for(i=1;i<=n;i++)	ans=min(ans,f[1][i]);
    	printf("%d",ans+K);
    	return 0;
    }
  • 相关阅读:
    纪念,暑假的招手
    #include <filename.h> 和 #include“filename.h” 有什么区别
    const 有什么用途
    指针和引用的区别
    main函数执行前后还会发生什么
    exit()和return语句的区别
    电子邮件中域名的使用和建议
    提升电子邮件产能的4个重要指标
    发件人信誉度培养
    tokyo cabinet相关站点文档
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7642643.html
Copyright © 2020-2023  润新知