• w


    前言

    几乎送我退役的一道题

    仔细想想其实不难 水一篇博客吧

    题目

    样例输入

    样例输入1

    5
    2 1 1 0
    1 3 0 1
    2 4 1 2
    5 2 1 1
    

    样例输出1

    1 2
    

    样例解释1

    操作路径 {2, 1, 3}。
    

    样例输入2

    3
    1 3 1 2
    2 1 0 0
    

    样例输出2

    0 0
    

    样例输入3

    6
    1 3 0 1
    1 2 0 2
    2 4 1 0
    4 5 1 0
    5 6 0 2
    

    样例输出3

    1 4
    

    什么破题,OJ上都没有!

    讲解

    首先我们可以很容易证明一个结论:每条边最多只会被翻转一次

    为了讲解方便,我们令边权为0,1的边分别为不需要和需要翻转的边,2为无所谓的边

    我们来从易到难解决问题

    对于第四组数据,不用脑子,输出0即可

    对于第三组数据,由于我们优先要满足操作次数少,所以我们要将被1边夹住的2边当做1边,而与0边相邻的2边当做0边,即可最优

    对于第五组数据,是一个比较简单的树形DP

    根据我们之前得到的结论,令dp[x]为使得x的子树内的边全部到达目标状态需要的操作数量和翻转长度

    红色:不需要翻转的链,橙色:需要翻转的链

    我们发现每两条需要翻转的链可以合并成一条,所以我们再用一个数组统计是否有从下面来的橙链,然后合并累加答案即可

    对于第一组数据,我们可以枚举翻转的边,然后用上述方法求答案即可

    对于第二组数据,同理,枚举是否翻转d=2的边,然后求答案

    80pts了!

    其实正解已经不远了,甚至跟部分分很相似

    我们仍然可以从组合橙链来思考,令(dp[x][0/1])表示在节点x,是否翻转父边的答案(操作数与长度都要存)

    转移详情见代码

    代码

    struct node
    {
    	int f,len;
    	node(){}
    	node(int f1,int len1){
    		f = f1;
    		len = len1;
    	}
    	node operator + (const node &a){
    		return node(f+a.f,len+a.len);
    	}
    	bool operator < (const node &a)const{
    		if(f < a.f || (f == a.f && len < a.len)) return 1;
    		return 0;
    	}
    }dp[MAXN][2];
    
    int head[MAXN],tot;
    struct edge
    {
    	int v,w,nxt;
    }e[MAXN << 1];
    void Add_Edge(int x,int y,int z)
    {
    	e[++tot].v = y;
    	e[tot].w = z;
    	e[tot].nxt = head[x];
    	head[x] = tot;
    }
    void Add_Double_Edge(int x,int y,int z)
    {
    	Add_Edge(x,y,z);
    	Add_Edge(y,x,z);
    }
    
    void dfs(int x,int fa,int d)//当前节点,父节点,父边的翻转状态 
    {
    	node t1 = node(INF,INF),t2 = node(0,0);//t1:有剩余橙链,t2:无剩余橙;链 
    	//这个循环是在组合子节点的状态 
    	for(int i = head[x]; i ;i = e[i].nxt)
    	{
    		if(e[i].v == fa) continue;
    		dfs(e[i].v,x,e[i].w);
    		node tmp1 = Min(t1+dp[e[i].v][0],t2+dp[e[i].v][1]);//1条橙链与0条橙链,0条橙链与1条橙链 组合是1条橙链 
    		node tmp2 = Min(t1+dp[e[i].v][1],t2+dp[e[i].v][0]);//1条橙链与1条橙链,0条橙链与0条橙链 组合是0条橙链 
    		t1 = tmp1;
    		t2 = tmp2;
    	}
    	//更新父边不翻转状态 : 
    	if(d == 1) dp[x][0] = node(INF,INF);
    	//父边必须被翻转,不能不翻,GG 
    	else dp[x][0] = Min(node(t1.f+1,t1.len),t2);
    	//父边不一定被翻转,即可从 “有一条剩余橙链并把它翻转”与 “无剩余橙链 ”转移而来 
    	
    	//更新父边要翻转状态 : 
    	if(!d) dp[x][1] = node(INF,INF);
    	//父边不能翻转,GG 
    	else dp[x][1] = Min(node(t1.f,t1.len+1),node(t2.f+1,t2.len+1));
    	//父边不一定不翻转,即可从“与儿子的一条剩余橙边组合” 与 “无剩余橙链,翻转自己” 转移而来 
    }
    
    int main()
    {
    //	freopen("w.in","r",stdin);
    //	freopen("w.out","w",stdout);
    	n = Read();
    	for(int i = 1;i < n;++ i)
    	{
    		int u = Read(),v = Read(),c = Read(),d = Read();
    		if(d == 2) Add_Double_Edge(u,v,2);
    		else Add_Double_Edge(u,v,c^d);
    	}
    	dfs(1,0,2);
    	Put(dp[1][0].s >> 1,' ');Put(dp[1][0].len);
    	//由于我们在合并两条橙链的时候,两条链都被统计进了方案,但是我们是要合并的,所以要除以二 
    	return 0;
    }
    
  • 相关阅读:
    毕业设计每日博客——第八周4
    毕业设计每日博客——第八周3
    毕业设计每日博客——第八周2
    毕业设计每日博客——第八周1
    毕业设计每日博客——第七周5
    毕业设计每日博客——第七周4
    毕业设计每日博客——第七周3
    毕业设计每日博客——第七周2
    毕业设计每日博客——第七周1
    毕业设计每日博客——第六周5
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/13567154.html
Copyright © 2020-2023  润新知