zoukankan      html  css  js  c++  java
  • 关于一些伪树上dp(贪心)

    前言:最近做了好多树上的类似dp的东西,今天是整理正好做个总结

    共同点:都是一种可以说是贪心的思想,能在子树中解决的都在子树中解决,解决不了的交给该子树的父节点的那颗子树中解决

    题目概述:
    这题太虎了,所以没有背景。
    给你一棵树,边有黑白两种颜色,你每次可以选择两个点,把这两个点之间的唯一简单路径上的所有边颜色取反,某些边要求最终颜色必须是黑色,还有些边没有要求,问最少操作多少次能达到目的
    第一行一个整数n,代表点数
    接下来 (n - 1) 行,每行三个数 (x, y, z),代表点 (i)(x) 之间有一条边,若 (y) 为 0 代表初始为白色,否则为黑色,若 (z) 为 0 代表不对最终颜色做要求,否则代表要求为黑色。

    思路

    很显然的对于一个子树跟节点而言,与它相连的所有的边(通向子节点),要是不做要求,那么有可能会被翻转,要是要求变黑并且原来是白色的,那么一定不会被翻转,如果一开始就是黑色的,那么一定不翻转。
    考虑哪些情况在子树中找不到配对的可以上传父节点:
    1:该子树与父节点的连边原本就需要翻转
    2:该子树与父节点的连边可以翻转也可以不翻转(不做要求)
    然后我们就dfs解决就好了

    代码实现

    #include <cstdio>
    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 1e6 + 50;
    inline int read () {
    	int x = 0, f = 1; char ch = getchar();
    	for (;!isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
    	for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
    	return x * f;
    }
    int n;
    struct Edge {
    	int to, next, judge, special;
    } edge[maxn << 1];
    int tot, head[maxn];
    void addedge (int a, int b, int col, int spc) {
    	edge[++tot].to = b;
    	edge[tot].next = head[a];
    	head[a] = tot;
    	if (col == 1) {
    		edge[tot].judge = 1;
    	} else if (spc == 1) {
    		edge[tot].special = 1;
    	}
    }
    int siz[maxn];
    int cnt;
    int fa[maxn];
    void dfs (int u, int f, int opt, int judge) {
    	for (register int i = head[u]; i; i = edge[i].next) {
    		int v = edge[i].to;
    		if (v == f) continue;
    		fa[v] = u;
    		if (edge[i].judge == 1) {
    			siz[u]++;
    		}
    		if (edge[i].judge == 1) {
    			dfs (v, u, 1, 0);
    		} else if (edge[i].special == 1) {
    			dfs (v, u, 0, 1);
    		} else {
    			dfs (v, u, 0, 0);
    		}
    	}
    	cnt += siz[u] / 2;
    	siz[u] %= 2;
    	if (siz[u] == 0) {
    	} else if (siz[u] == 1 && opt == 1) {
    	} else if (opt == 0 && judge == 1){
    		siz[fa[u]]++;
    	} else if (siz[u] == 1 && opt == 0 && judge == 0) {
    		cnt++;
    	}
    }
    int main () {
    	n = read();
    	int x, y, z;
    	for (register int i = 2; i <= n; i++) {
    		x = read(), y = read(), z = read();
    		if (z == 0) {
    			addedge (i, x, 0, 1);
    			addedge (x, i, 0, 1);
    		} else if (y == 0 && z != 0) {
    			addedge (i, x, 1, 0);
    			addedge (x, i, 1, 0);
    		} else {
    			addedge (i, x, 0, 0);
    			addedge (x, i, 0, 0);
    		}
    	}
    	dfs (1, 0, 0, 0);
    	cout<<cnt<<endl;
    	return 0;
    }
    

    Graph

    给定一张 (n) 个点, m条边的无向图,每条边连接两个顶点,保证无重边自环,不保证联通。
    你想在这张图上进行若干次旅游,每次旅游可以任选一个点作为起点,再走到一个与 (x) 直接有边相连的点 (y) , 再走到一个与 (y) 有边相连的点 (z) 并结束本次旅游。
    作为一个旅行爱好者,你不希望任意一条边超过一次, 注意一条边不能正向走一次,反向走一次,点可以经过多次,满足该情况你希望尽可能多次的旅游,请计算出最多的旅行次数,并任意输出一种方案。

    思路

    很显然对于一个联通块来说答案就是边数 % 3, 你就可以发现一种最有的贪心策略,可以在字数中找到配对的就在子树中解决,找不到的就传到父节点中,在父节点找配对的边,注意要优先解决这种在子树中找不到配对的边,因为如果上传父节点之后还是没有配对,那么这条边就不可以再次上传了,并且这种边一定可以在上传父节点时找到配对。
    (vector) 随便维护一下就好了

    代码实现

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int maxn = 1e5 + 50;
    inline int read () {
    	int x = 0, f = 1; char ch = getchar();
    	for (;!isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
    	for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
    	return x * f;
    }
    int n, m;
    struct Edge {
    	int from, to, next;
    	bool judge;
    } edge[maxn << 2];
    int tot, head[maxn];
    void addedge (int a, int b) {
    	edge[++tot].to = b;
    	edge[tot].from = a;
    	edge[tot].next = head[a];
    	head[a] = tot;
    }
    bool vis[maxn];
    int cnt;
    struct Node {
    	int fro, midd, too;
    }node[maxn];
    vector<pair<int, int> > vec[maxn];
    int fa[maxn];
    void dfs (int u) {
    	vis[u] = true;
    	for (register int i = head[u]; i; i = edge[i].next) {
    		int v = edge[i].to;
    		if (edge[i].judge == true) continue;
    		edge[i].judge = true;
    		if (edge[i + 1].from == edge[i].to && edge[i + 1].to == edge[i].from) {
    			edge[i + 1].judge = true;
    		} else {
    			edge[i - 1].judge = true;
    		}
    		if (vis[v]) {
    			vec[u].push_back(make_pair(u, v));
    			continue;
    		}
    		fa[v] = u;
    		dfs (v);
    		vec[u].push_back(make_pair(u, v));
    	}
    	int siz = vec[u].size();
    	for (register int i = 0; i < vec[u].size(); i++) {
    		int x = vec[u][i].first, y = vec[u][i].second;
    		if (x != u && y != u){
    			int xx = vec[u][i + 1].first, yy = vec[u][i + 1].second;
    			if (xx == x) {
    				node[++cnt] = (Node) {yy, xx, y};
    			} else if (yy == x) {
    				node[++cnt] = (Node) {xx, yy, y};
    			} else if (xx == y){
    				node[++cnt] = (Node) {x, xx, yy};
    			} else {
    				node[++cnt] = (Node) {x, yy, xx};
    			}
    			i++;
    		} else if (i + 1 < siz) {
    			int xx = vec[u][i + 1].first, yy = vec[u][i + 1].second;
    			if (xx == u) {
    				node[++cnt] = (Node) {y, x, yy};
    				i++;
    			}
    			else {
    				vec[u].push_back(make_pair(x, y));
    				siz++;
    			}
    		} else {
    			vec[fa[u]].push_back(make_pair(x, y));
    		}
    	}
    }
    int main () {
    	n = read(), m = read();
    	int x, y;
    	for (register int i = 1; i <= m; i++) {
    		x = read(), y = read();
    		addedge (x, y), addedge (y, x);
    	}
    	for (register int i = 1; i <= n; i++) {
    		if (vis[i] == false) dfs(i);
    	}
    	cout<<cnt<<endl;
    	for (register int i = 1; i <= cnt; i++) {
    		printf("%d %d %d
    ", node[i].fro, node[i].midd, node[i].too);
    	}
    	return 0;
    }
    

    凉宫春日的消失

    在观察凉宫和你相处的过程中, (Yoki) 产生了一个叫做爱的 (bugfeature) ,将自己变成了一个没有特殊能力的普通女孩并和你相遇。但你仍然不能扔下凉宫,准备利用 (Yoki) 留下的紧急逃脱程序回到原来的世界。这个紧急逃脱程序的关键就是将线索配对。
    为了简化问题,我们将可能的线索间的关系用一棵n个点的树表示,两个线索的距离定义为其在树上唯一最短路径的长度。因为你不知道具体的线索是什么,你需要进行 (q) 次尝试,每次尝试都会选中一个大小为偶数的线索集合 (V) ,你需要将线索两两配对,使得配对线索的距离之和不超过 (n) 。如果这样的方案不存在,输出 (No)

    思路

    一眼看到选关键点,显然可以用虚树搞,并且很显然有一个性质,该条件只要关键点数是偶数,那么一定存在方案。一个类似贪心的思想,可以在一颗子树中找到配对的就在一颗子树中解决,并且一颗子树中最多只会有一个点没有找到配对,那么把当前点扔到父节点中找配对,并且这个点选最靠上的

    代码实现

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    using namespace std;
    const int maxn = 2e5 + 50;
    inline int read () {
        int x = 0, f = 1; char ch = getchar();
        for (;!isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
        for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - '0';
        return x * f;
    }
    int n;
    struct Edge {
        int from, to, next;
    } edge[maxn << 1];
    int tot, head[maxn];
    inline void addedge (int a, int b) {
        edge[++tot].to = b;
        edge[tot].from = a;
        edge[tot].next = head[a];
        head[a] = tot;
    }
    deque<int> que[maxn];
    bool col[maxn];
    int f[maxn];
    int son[maxn], siz[maxn], deep[maxn];
    void dfs1 (int u) {
        siz[u] = 1;
        for (register int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].to;
            if (v == f[u]) continue;
            f[v] = u;
            deep[v] = deep[u] + 1;
            dfs1 (v);
            siz[u] += siz[v];
            if (siz[v] > siz[son[u]]) son[u] = v;
        }
    }
    int dfn[maxn], dfn_clock;
    int top[maxn];
    void dfs2 (int u) {
        dfn[u] = ++dfn_clock;
        if (son[u]) {
            top[son[u]] = top[u];
            dfs2 (son[u]);
            for (register int i = head[u]; i; i = edge[i].next) {
                int v = edge[i].to;
                if (v != f[u] && v != son[u]) {
                    top[v] = v;
                    dfs2 (v);
                }
            }
        }
    }
    inline int lca (int x, int y) {
        while (top[x] != top[y]) {
            if (deep[top[x]] > deep[top[y]]) {
                x = f[top[x]];
            } else {
                y = f[top[y]];
            }
        }
        if (deep[x] < deep[y]) return x;
        return y;
    }
    int tp;
    int stk[maxn];
    inline void ins(int x)
    {
        if (tp == 0)
        {
            stk[tp = 1] = x;
            return;
        }
        int ance = lca(stk[tp], x);
        while ((tp > 1) && (deep[ance] < deep[stk[tp - 1]]))
        {
            addedge(stk[tp - 1], stk[tp]);
            --tp;
        }
        if (deep[ance] < deep[stk[tp]]) addedge(ance, stk[tp--]);
        if ((!tp) || (stk[tp] != ance)) stk[++tp] = ance;
        stk[++tp] = x;
    }
    int a[maxn];
    inline void dfs (int u) {
    	stk[++tp] = u;
        for (register int i = head[u]; i; i = edge[i].next) {
            int v = edge[i].to;
            dfs(v);
            if (!que[v].empty()) {
                int a = que[v].front();
                que[v].pop_front();
                que[u].push_back(a);
            }
        }
        if (col[u]) que[u].push_front(u);
        while (!que[u].empty()) {
            int a = que[u].back();
            que[u].pop_back();
            if (!que[u].empty()) {
                int b = que[u].back();
                que[u].pop_back();
                printf("%d %d
    ", a, b);
            } else {
                que[u].push_back(a);
                break;
            }
        }
    }
    bool cmp (int a, int b) {
        return dfn[a] < dfn[b];
    }
    int main () {
        n = read();
        int x, y;
        for (register int i = 1; i < n; i++) {
            x = read(), y = read();
            addedge (x, y), addedge (y, x);
        }
        int s;
        dfs1 (1);
        dfs2 (1);
        memset (head, 0, sizeof head);
        tot = 0;
        while (1) {
            s = read();
            if (s == 0) return 0;
            for (register int i = 1; i <= s; i += 1) {
                a[i] = read();
                col[a[i]] = true;
            }
            printf("Yes
    ");
            sort (a + 1, a + 1 + s, cmp);
            if (a[1] != 1) {
                stk[tp = 1] = 1;
            }
            for (register int i = 1; i <= s; i++) {
                ins (a[i]);
            }
            if (tp) {
                while (--tp) {
                    addedge (stk[tp], stk[tp + 1]);
                }
            }
            tp = 0;
            dfs (1);
            for (register int i = 1; i <= tp + 1; i++) {
            	head[stk[i]] = 0;
            	col[stk[i]] = false;
            }
            tp = 0;
            tot = 0;
        }
        return 0;
    }
    

    完结散花(QwQ), (csp rp++)

  • 相关阅读:
    elasticsearch 5.x 系列之七 基于索引别名的零停机升级服务
    Linux 查看系统硬件信息(实例详解)
    linux grep命令详解
    Boot loader: Grub进阶(转)
    Boot loader: Grub入门(转)
    内核模块管理(转)
    Centos启动流程(转)
    Linux 内核启动流程(转)
    程序的运行顺序(转)
    查询进程打开的文件(转)
  • 原文地址:https://www.cnblogs.com/hzoi-liujiahui/p/13800890.html
Copyright © 2011-2022 走看看