zoukankan      html  css  js  c++  java
  • 牛客多校2020 第十场 Identical Trees

    神奇的树形DP+二分图最大权值匹配转移

    dp[x][y]表示以左边的树x为根,右边的树y为根,他们有dp[x][y]个序号是重合的,若x和y不同构那就dp[x][y] = -INF;

    如何转移?

    给x的儿子们和y的儿子们建个二分图跑最大权值匹配,跑下来的最大权值就是儿子们的答案,很好笑,但是复杂度有点玄学不敢写罢了,

    本人代码弱智,易于理解,详细的看代码把,

    #include<iostream>
    #include<vector>
    #include<algorithm>
    #include<cstring>
    #include<unordered_map>
    #include<queue>
    #include<string>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    const int maxn = 3020;
    int n, m;
    queue<int> que;
    int head[maxn];
    
    unordered_map<string, int>id;
    
    struct Edge {
        int id, nex;
        int w, f;
    }edge[40400];
    
    int cnt = 0;
    
    
    void init() {
        cnt = 0;
        memset(head, -1, sizeof(head));
        while (que.size()) que.pop();
    }
    
    void add_(int x, int y, int f, int w) {
        edge[cnt].id = y;
        edge[cnt].f = f;
        edge[cnt].w = w;
        edge[cnt].nex = head[x];
        head[x] = cnt++;
    }
    
    void add(int x, int y, int f, int w) {
        add_(x, y, f, w);
        add_(y, x, 0, -w);
    }
    int vis[maxn];
    int dis[maxn];
    int mflow[maxn];
    int per[maxn];
    
    int spfa(int s, int t) {
        memset(vis, 0, sizeof(vis));
        memset(dis, inf, sizeof(dis));
        mflow[s] = inf;
        que.push(s);
        dis[s] = 0;
        vis[s] = 1;
        while (que.size()) {
            int x = que.front();
            que.pop();
            vis[x] = 0;
            for (int i = head[x]; ~i; i = edge[i].nex) {
                int v = edge[i].id;
                if (dis[v] > dis[x] + edge[i].w && edge[i].f) {
                    dis[v] = dis[x] + edge[i].w;
                    per[v] = i;
                    mflow[v] = min(mflow[x], edge[i].f);
                    if (vis[v]) continue;
    
                    vis[v] = 1;
                    que.push(v);
                }
            }
        }
        if (dis[t] != inf)  return 1;
        else return 0;
    }
    
    void update(int s, int t, int& flow) {
        int minn = mflow[t];
        for (int i = t; i != s; i = edge[per[i] ^ 1].id) {
    
            int x = per[i];
            edge[x].f -= minn;
            edge[x ^ 1].f += minn;
        }
        flow += minn;
    }
    
    int solve(int s, int t, int& flow) {
        int ans = 0;
        while (spfa(s, t)) {//用spfa开路
            ans += dis[t] * mflow[t];
            update(s, t, flow);
        }
        return ans;
    }
    
    
    ll dp[550][550];
    vector<int>G1[maxn], G2[maxn];
    int jude[550][550];
    int aa = 0;
    int cnt1[550], cnt2[550];
    
    
    int dfs(int x, int y) {
        //看看x和y是否同构
        if (jude[x][y] == 0) {
            dp[x][y] = -inf;
            return 0;
        }
    
    
        if (x == y) {
            dp[x][y] = 1;
            if (G1[x].size() == 0) return 0;
        }
    
        for (int i = 0; i < G1[x].size(); i++) {
            for (int j = 0; j < G2[y].size(); j++) {
                int a = G1[x][i];
                int b = G2[y][j];
                dfs(a, b);
            }
        }
        init();
    
        for (int i = 0; i < G1[x].size(); i++) add(2010, i, 1, 0);
        for (int j = 0; j < G2[y].size(); j++) add(j + 505, 2011, 1, 0);
    
        for (int i = 0; i < G1[x].size(); i++) {
            for (int j = 0; j < G2[y].size(); j++) {
                int a = G1[x][i];
                int b = G2[y][j];
                add(i, j + 505, 1, -dp[a][b]);
            }
        }
        int cns = 0;
    
        int ans = solve(2010, 2011, cns);
        dp[x][y] -= ans;
    
        //cout << x << "----------" << y << ":	" << dp[x][y] <<"	"<< cns << endl;
        return 0;
    }
    
    int get(int x, int y) {
    
        for (int i = 0; i < G1[x].size(); i++) {
            for (int j = 0; j < G2[y].size(); j++) {
                int a = G1[x][i];
                int b = G2[y][j];
                get(a, b);
            }
        }
        string sn;
        if (G1[x].size() == G2[y].size()) {
            if (G1[x].size() == 0) {
                jude[x][y] = 1;
            }
            else {
                for (int p : G1[x]) {
                    sn += (char)(cnt1[p] + '0');
                }
                sort(sn.begin(), sn.end());
                if (id[sn] == 0) id[sn] = ++aa;
                cnt1[x] = id[sn];
                sn.clear();
    
                for (int p : G2[y]) {
                    sn += (char)(cnt2[p] + '0');
                }
                sort(sn.begin(), sn.end());
                if (id[sn] == 0) id[sn] = ++aa;
                cnt2[y] = id[sn];
                sn.clear();
    
                if (cnt1[x] == cnt2[y]) jude[x][y] = 1;
            }
        }
    
        return 0;
    }
    int main() {
        scanf("%d", &n);
        int root1, root2;
        int x;
        for (int i = 1; i <= n; i++) {
            scanf("%d", &x);
            if (x == 0) root1 = i;
            else G1[x].push_back(i);
        }
        for (int i = 1; i <= n; i++) {
            scanf("%d", &x);
            if (x == 0) root2 = i;
            else G2[x].push_back(i);
        }
    
        for (int i = 1; i <= n; i++) dp[i][i] = 1;
        get(root1, root2);
    
        dfs(root1, root2);
    
        cout << n - dp[root1][root2] << endl;
    }
    /*
    7
    0 1 1 2 2 3 3
    0 1 1 3 3 2 2
    输出2
    
    
    */
    寻找真正的热爱
  • 相关阅读:
    微信小程序开发 —— 一些小的注意点
    C# —— 继承
    JavaScript —— 判断一个对象是否为空
    SQLServer——事务
    创建存储过程
    (转)从开发小白到音视频专家
    (转载)从零开始学习音视频编程技术(一) 视频格式讲解
    (转载)如何正确的阅读FFmpeg官网提供的资料
    (转载)音频编解码基础(wav/aac/pcma/pcmu)
    (转载)语音编解码过程概述
  • 原文地址:https://www.cnblogs.com/lesning/p/13476608.html
Copyright © 2011-2022 走看看