• [IOI2005]Riv 河流


    XL.[IOI2005]Riv 河流

    新转移方式get~~~

    我必须吐槽一下现在赞最多的那篇题解,虽然思路巧妙,但是明显没有“物尽其用”,对于各DP数组的真实含义也没有把握清楚。

    一个naive的想法就是:设\(f[i][j]\)表示:在\(i\)的子树中,修了\(j\)个场子,的最小费用。

    但是这样不是很好转移——子树传上来的信息不能直接合并,因为我们必须知道场子到底修哪了才能准确得出答案。

    而我们又不可能在状态里面维护这么多场子——状压不了。

    等等,我们为什么要从根记录子树,为什么不是从子树记录根?

    我们设\(f[x][j][k]\)表示:

    \(x\)为根的子树中,修了\(k\)个堡。并且,强制在第\(j\)个点修个堡(\(j\)\(i\)的祖先)。

    这样,合并子树时就可以直接背包了——因为每个节点流到的堡确定了,代价自然就可以提前算出。

    即:

    \(f[x][j][k]=\max\{f[x][j][l]+f[y][j][k-l]\},\text{y is a son of x}\)

    每次将\(x\)的状态同\(y\)合并。

    但这样就会出现一些问题——我们说要在\(j\)修个堡,但是这只是空头支票,没有算到\(k\)里面,当访问到\(j\)时,这个债是要还的!

    因此对于\(f[x][x][k]\),我们应该让\(k\)全体右移一位,即\(f[x][x][k]=f[x][x][k-1]\),且\(f[x][x][0]=\infty\)(欠的一个堡还不回来,只能破产)。

    还有,我们要计算\(x\)位置新产生的代价。这个代价要么\(x\)位置额外再修一个堡,要么就是到\(j\)的距离。因此我们有

    \(f[x][j][k]=\min\Big(f[x][j][k]+val_x*(dis_x-dis_k),f[x][x][k]\Big)\)

    则答案为\(f[0][0][K+1]\)\(0\)号点有个免费的堡)。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,head[110],cnt,f[110][110][60],g[60],val[110],dis[110],anc[110],tp;
    struct node{
    	int to,next,val;
    }edge[210];
    void ae(int u,int v,int w){
    	edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;
    }
    void dfs(int x){
    	anc[++tp]=x;
    	for(int i=head[x],y;i!=-1;i=edge[i].next){
    		y=edge[i].to,dis[y]=dis[x]+edge[i].val,dfs(y);
    		for(int j=1;j<=tp;j++){
    			for(int k=0;k<=m;k++)g[k]=0x3f3f3f3f;
    			for(int k=0;k<=m;k++)for(int l=0;l<=k;l++)g[k]=min(g[k],f[x][anc[j]][k-l]+f[y][anc[j]][l]);
    			for(int k=0;k<=m;k++)f[x][anc[j]][k]=g[k];
    		}
    	}
    	for(int j=m;j;j--)f[x][x][j]=f[x][x][j-1];
    	f[x][x][0]=0x3f3f3f3f;
    	for(int j=1;j<tp;j++)for(int k=0;k<=m;k++)f[x][anc[j]][k]+=val[x]*(dis[x]-dis[anc[j]]),f[x][anc[j]][k]=min(f[x][anc[j]][k],f[x][x][k]);
    	tp--;
    }
    int main(){
    	scanf("%d%d",&n,&m),m++,memset(head,-1,sizeof(head));
    	for(int i=1,x,y;i<=n;i++)scanf("%d%d%d",&val[i],&x,&y),ae(x,i,y);
    	dfs(0);
    //	for(int i=1;i<=n;i++)printf("%d ",dis[i]*val[i]);puts("");
    	printf("%d\n",f[0][0][m]);
    	return 0;
    }
    

  • 相关阅读:
    (todo)数组名 有存储空间吗?
    c和指针 指针数组 关于指针数组
    c面试题 来自android手机 1/6
    c 和指针 二维数组赋予一维数组指针 数组长度 数组与指针长度区别
    c语言优先级面试小结
    hxf 每晚 宏 不用大于小于 求大小
    DelphiX中的DXSprite单元中涉及到修改
    在java开发过程中,添加表时,需要配置一下的目录和文件。
    代码生成器cs的注册方法.
    将一个字符串映射为一个Delphi页面控件属性名
  • 原文地址:https://www.cnblogs.com/Troverld/p/14597204.html
Copyright © 2020-2023  润新知