• 【题解】[IOI2005]Riv 河流


    题目戳我

    ( ext{Solution:})

    考虑显然树形(dp).

    1.设(dp[i][j])表示子树(i)选了(j)个的最小代价。

    发现转移时的(cost)没法转移。

    2.设(dp[i][j][k])表示子树(i)选了(k)个,距离(i)最近的伐木场是(j)的最小代价。

    不方便处理(i)是不是被选中的情况。

    3.设(dp[i][j][k][0/1])表示的是(i)是不是建立的伐木场,其他对应第三种情况。

    考虑转移:

    首先,计算两点距离可以树上差分,由于我们这里要计算的两点距离都在一条链上,所以(dis=disroot_i-disroot_j.)

    对于(i)不选:枚举当前已经选择了多少个,此时对于最近节点(j,vin son_i)的最近节点也必然为(j.)所以要从(dp[v][j]..)状态转移。

    对于(i)选:这里(v)最近的就是(x)了。

    考虑对当前节点(dp)完后的处理过程:将某些(dp)状态完全计算掉,对于需要把木头运到(j)的要把代价计算上。

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=2e3+10;
    int n,k,tot,head[MAXN],ans;
    struct E{int nxt,to,dis,v;}e[MAXN];
    inline void add(int x,int y,int w){
    	e[++tot]=(E){head[x],y,w};
    	head[x]=tot;
    }
    int dp[102][102][51][2],wd[5001];
    int dis[500],pa[500],cnt,ac[1001];
    void dfs(int x,int fa){
    	pa[x]=fa;
    	for(int i=head[x];i;i=e[i].nxt){
    		int j=e[i].to;
    		if(j==fa)continue;
    		dis[j]=dis[x]+e[i].dis;
    		dfs(j,x);
    	}
    }
    //dis[i]表示1-i的距离 预处理dis&pa 
    void DP(int x){
    	ac[++cnt]=x;
    	//dp[i][j][k][0/1] means that the pos i is or not built a base,the nearest pos to i is j,i's tree has been build k
    	//意味着i及其子树一共建立了k个,距离i最近的是j,i是不是建立了的最小代价 
    	for(int i=head[x];i;i=e[i].nxt){
    		int v=e[i].to;
    		if(v==pa[x])continue;
    		DP(v);
    		for(int j=tot;j>=1;--j){
    			int fa=ac[j];
    			//枚举所有祖先 
    			for(int u=k;u>=0;--u){
    				//注意倒序更新,因为要从旧状态转移 
    				dp[x][fa][u][0]+=dp[v][fa][0][0];
    				//当前点没有建立,所以v最近的也是fa
    				//这里第三维是0 单独处理 即v及其子树没有选 
    				dp[x][fa][u][1]+=dp[v][x][0][0];
    				//同理 x选了 v最近的就是x了 
    				for(int l=u;l>=0;--l){
    					dp[x][fa][u][0]=min(dp[x][fa][u][0],dp[v][fa][l][0]+dp[x][fa][u-l][0]);
    					//对于当前x没选的,那v最近的还是fa,这里dp[x][fa][u-l][0]是v子树之前x选了u-l个 
    					dp[x][fa][u][1]=min(dp[x][fa][u][1],dp[v][x][l][0]+dp[x][fa][u-l][1]);
    					//对于当前x选;1的,其实就是v最近的改成x,以及记住选择了x即可 
    					//考虑枚举选择了几个 
    				}
    			}
    		}
    	}
    	for(int i=1;i<=tot;++i){
    		int fa=ac[i];
    		for(int j=k;j>=0;--j){
    			if(j>=1)dp[x][fa][j][0]=min(dp[x][fa][j][0]+wd[x]*(dis[x]-dis[fa]),dp[x][fa][j-1][1]);
    			else dp[x][fa][j][0]+=wd[x]*(dis[x]-dis[fa]);
    			//注意合并状态,对于i没建立的要把木头移到j上,建立了的就不需要,但是注意第三维的限制 
    		}
    	}
    	cnt--;//当前点已经不会再成为祖先了 
    }
    int main(){
    	scanf("%d%d",&n,&k);
    	for(int i=2;i<=n+1;++i){
    		int x,y,z;
    		scanf("%d%d%d",&x,&y,&z);
    		y++;wd[i]=x;//记木材 
    		add(i,y,z);add(y,i,z);
    	}
    	dfs(1,0);DP(1);
    	printf("%d
    ",dp[1][1][k][0]);
    	return 0;
    } 
    
  • 相关阅读:
    技术学习沙龙
    mysql升级5.5
    mysql用户权限管理的问题
    dwz(jui)刷新当前dialog的方法
    perl进程管理一例
    cron执行service
    tp数据库表大写命名的一些问题
    php执行多个存储过程
    thinkphp使用中遇到的问题
    html5 ajax 文件上传
  • 原文地址:https://www.cnblogs.com/h-lka/p/13888360.html
Copyright © 2020-2023  润新知