• hihocoder 1035 : 自驾旅行 III


    描述
    给定一棵含有 n 个结点的树,结点从 1 标号。你从 1 号结点驾车出发,希望遍历一些关键结点(访问到就好,不需要按照这些关键结点的输入顺序)。每条边有两个权值,c0, c1 分别表示步行和驾车经过这条边的代价。每次你可以选择驾车经过一条边(当且仅当有车),或者将车停放在当前所在的结点(如果有车),步行经过一条边。
    求遍历完所有关键结点的最少代价。
    注意: 你可以在任意结点结束遍历,即使当前没有车。

    解题报告:
    用时:2h20min,1RE1WA
    这题做的不是很好,首先看到题目思考了许久找不到好一点的状态,然后参考了题解的状态
    (f[x][0/1/2/3/4])表示x子树内所有的关键点都已经走完的5中状态的最小代价
    (f[x][0])表示只考虑人走,且人必须回到x的最小代价
    (f[x][1])表示只考虑人走,且人可以不回来的最小代价(用于f[x][4]的转移)
    (f[x][2])表示人车一起走,且两者都回来的最小代价
    (f[x][3])表示人车一起走,且人回车不回的最小代价(用于f[x][4]的转移)
    (f[x][4])表示人车一起走,最后两者都不回来的最小代价
    在自己做的时候并没有定义到(f[x][3])这个状态,然后发现(f[x][4])是可以借助(f[x][3])转移的

    (dis)为该边人走的代价,(dis0)为车走的代价,(u)为x的子节点
    (f[x][0]=sum_{u}f[u][0]+2*dis)
    (f[x][2]=sum_{u}Min(f[u][2]+2*dis0,f[u][0]+2*dis))
    这两个比较显然,对于(f[x][2])你可以带着车一起走完回来,也可以把车放在原地,走完回来
    (t=(f[u][2]+2*dis0,f[u][0]+2*dis))
    (f[x][1])就是某一个子节点走的是(f[u][1]+dis),其他节点走的是(t)
    (f[x][3])同理,某一个点走(f[u][3]+dis+dis0),其他点走(t)
    显然这两个节点的特殊节点的选择都是选择贡献最大的,即(f[u][1]+dis)(f[u][0]+dis*2)做差后最大的一个
    对于(f[x][4])两者都不回,我们要分情况讨论:
    1.在遍历最后一颗子树时,有车
    显然遍历之前是(f[x][3]),然后可以选择最后一颗子树是开车还是不开车对应(f[u][1])(f[u][4])
    2.若此时没有车
    遍历之前状态是(f[x][2]),其中某个子树是(f[u][3]),表示车没有回来人回来了,然后和上面一种情况不同的是:只能选择人走了,那么就是在不同于选择了(f[u][3])的子树中再选择一个走(f[v][1])
    一个我没注意到的细节:
    如果u同于v,那么应该记录一个次小值,不然就会少一组转移

    #include <algorithm>
    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #define RG register
    #define il inline
    #define iter iterator
    #define Max(a,b) ((a)>(b)?(a):(b))
    #define Min(a,b) ((a)<(b)?(a):(b))
    using namespace std;
    typedef long long ll;
    const int N=1e6+5;
    int head[N],num=0,to[N<<1],nxt[N<<1],dis[N<<1],dis0[N<<1];
    void link(int x,int y,int d,int d0){
    	nxt[++num]=head[x];to[num]=y;dis[num]=d;
    	dis0[num]=d0;head[x]=num;
    }
    int gi(){
    	int str=0;char ch=getchar();
    	while(ch>'9' || ch<'0')ch=getchar();
    	while(ch>='0' && ch<='9')str=(str<<1)+(str<<3)+ch-48,ch=getchar();
    	return str;
    }
    int n,m;bool vis[N],mark[N];ll f[N][5];
    void dfs(int x,int last){
    	int u,imp;ll t,f1=0,f2=0,f3=0,tmp,f4=0;
    	mark[x]=vis[x];
    	for(int i=head[x];i;i=nxt[i]){
    		u=to[i];if(u==last)continue;
    		dfs(u,x);
    		mark[x]+=mark[u];
    		if(!mark[u])continue;
    		f[x][0]+=f[u][0]+(dis[i]<<1);
    		t=Min(f[u][2]+(dis0[i]<<1),f[u][0]+(dis[i]<<1));
    		f[x][2]+=t;
    		f1=Min(f1,f[u][1]-f[u][0]-dis[i]);
    		tmp=f[u][3]+dis[i]+dis0[i]-t;
    		if(tmp<f2){
    			f4=f2;
    			f2=tmp;
    			imp=u;
    		}
    		else if(tmp<f4)f4=tmp;
    		f3=Min(f3,min(f[u][4]+dis0[i],f[u][1]+dis[i])-t);
    	}
    	f[x][1]=f[x][0]+f1;
    	f[x][3]=f[x][2]+f2;
    	f[x][4]=f[x][2]+f3;
    	f[x][4]=Min(f[x][4],f[x][3]);
    	for(int i=head[x];i;i=nxt[i]){
    		u=to[i];if(u==last || !mark[u])continue;
    		t=Min(f[u][2]+(dis0[i]<<1),f[u][0]+(dis[i]<<1));
    		tmp=f[u][1]+dis[i]-t;
    		if(u==imp)
    			f[x][4]=Min(f[x][4],f[x][2]+tmp+f4);
    		else f[x][4]=Min(f[x][4],f[x][2]+tmp+f2);
    	}
    }
    void work()
    {
    	int x,y,d,d0;
    	n=gi();
    	for(int i=1;i<n;i++){
    		x=gi();y=gi();d=gi();d0=gi();
    		link(x,y,d,d0);link(y,x,d,d0);
    	}
    	m=gi();
    	for(int i=1;i<=m;i++){
    		x=gi();vis[x]=true;
    	}
    	dfs(1,1);
    	printf("%lld
    ",Min(f[1][4],f[1][0]));
    }
    
    int main()
    {
    	work();
    	return 0;
    }
    
    
  • 相关阅读:
    黑产江湖
    FW/IDS/IPS/WAF等安全设备部署方式及优缺点
    SOAPA来临,SIEM时代终结?
    美国爱因斯坦计划4
    零基础如何学好安卓开发
    协同办公系统能为企业带来怎样的影响?
    阿里腾讯开撕,钉钉的广告打到腾讯的地盘了
    bug管理工具为开发者工作带来哪些改变?
    开发人员必备的几款bug管理工具
    教你玩转产品管理系统iClap(PC端功能篇)
  • 原文地址:https://www.cnblogs.com/Yuzao/p/7527289.html
Copyright © 2020-2023  润新知