• 暴力+网络流 [2020牛客暑期多校训练营(第十场)Identical Trees]


    暴力+网络流 2020牛客暑期多校训练营(第十场)Identical Trees

    题目大意:

    给你两棵大小是 (n) 的树,可以对 (T1) 进行操作,变成 (T2) ,每次操作可以选择一个节点,把这个节点改成任意一个你想换成的点,问最少的操作使得 (T1) 变成 (T2)

    题解:

    一个非常暴力的网络流,甚至不需要树哈希来判断是否同构,可以在网络流的过程中直接判断是否同构。

    递归,同时枚举两棵树的儿子节点,如果是同构,那么直接建边,每一层跑一次网络流。

    这个说不太清楚,主要看代码吧。

    #include <bits/stdc++.h>
    #define inf 0x3f3f3f3f
    #define debug(x) cout<<"debug:"<<#x<<" = "<<x<<endl;
    using namespace std;
    typedef long long ll;
    
    /*
    MCMF 最小费用最大流
    */
    const int INF = 0x3f3f3f3f;
    const int maxn = 1000 + 10;
    struct Edge
    {
        int u, v, c, f, cost;
        Edge(int u, int v, int c, int f, int cost):u(u), v(v), c(c), f(f), cost(cost){}
    };
    struct MCMF
    {
    	vector<Edge>e;
    	vector<int>G[maxn];
    	int a[maxn];//找增广路每个点的水流量
    	int p[maxn];//每次找增广路反向记录路径
    	int d[maxn];//SPFA算法的最短路
    	int inq[maxn];//SPFA算法是否在队列中
    	int n, m;
    	void init(int s1,int s2)
    	{
    
    	    e.clear();
    	    for(int i = 0; i <= s1+s2+1; i++)G[i].clear();
    	    int s = s1+s2,t = s1+s2+1;
    		for(int i=0;i<s1;i++) addEdge(s,i,1,0);
    		for(int i=s1;i<s1+s2;i++) addEdge(i,t,1,0);
    	}
    	void addEdge(int u, int v, int c, int cost)
    	{
    	    e.push_back(Edge(u, v, c, 0, cost));
    	    e.push_back(Edge(v, u, 0, 0, -cost));
    	    int m = e.size();
    	    G[u].push_back(m - 2);
    	    G[v].push_back(m - 1);
    	}
    	bool bellman(int s, int t, int& flow, long long & cost)
    	{
    	    for(int i = 0; i <= t + 1; i++) d[i] = INF,inq[i] = 0;
    	    d[s] = 0;inq[s] = 1;//源点s的距离设为0,标记入队
    	    p[s] = 0;a[s] = INF;//源点流量为INF(和之前的最大流算法是一样的)
    
    	    queue<int>q;//Bellman算法和增广路算法同步进行,沿着最短路拓展增广路,得出的解一定是最小费用最大流
    	    q.push(s);
    	    while(!q.empty())
    	    {
    	        int u = q.front();
    	        q.pop();
    	        inq[u] = 0;//入队列标记删除
    	        for(int i = 0; i < G[u].size(); i++)
    	        {
    	            Edge & now = e[G[u][i]];
    	            int v = now.v;
    	            if(now.c > now.f && d[v] > d[u] + now.cost)
    	                //now.c > now.f表示这条路还未流满(和最大流一样)
    	                //d[v] > d[u] + e.cost Bellman 算法中边的松弛
    	            {
    	                d[v] = d[u] + now.cost;//Bellman 算法边的松弛
    	                p[v] = G[u][i];//反向记录边的编号
    	                a[v] = min(a[u], now.c - now.f);//到达v点的水量取决于边剩余的容量和u点的水量
    	                if(!inq[v]){q.push(v);inq[v] = 1;}//Bellman 算法入队
    	            }
    	        }
    	    }
    	    if(d[t] == INF)return false;//找不到增广路
    	    flow += a[t];//最大流的值,此函数引用flow这个值,最后可以直接求出flow
    	    cost += (long long)d[t] * (long long)a[t];//距离乘上到达汇点的流量就是费用
    	    for(int u = t; u != s; u = e[p[u]].u)//逆向存边
    	    {
    	        e[p[u]].f += a[t];//正向边加上流量
    	        e[p[u] ^ 1].f -= a[t];//反向边减去流量 (和增广路算法一样)
    	    }
    	    return true;
    	}
    	int MincostMaxflow(int s, int t, long long & cost)
    	{
    	    cost = 0;
    	    int flow = 0;
    	    while(bellman(s, t, flow, cost));//由于Bellman函数用的是引用,所以只要一直调用就可以求出flow和cost
    	    return flow;//返回最大流,cost引用可以直接返回最小费用
    	}
    }ans;
    
    
     vector<int>G1[maxn];
     vector<int> G2[maxn];
     int dfs(int rt1,int rt2){
     	int s1 = G1[rt1].size(),s2 =G2[rt2].size();
     	if(s1!=s2) return -1;
     	vector<int>e[3];
     	e[0].clear(),e[1].clear(),e[2].clear();
     	for(int i=0;i<s1;i++){
     		for(int j=0;j<s2;j++){
     			int x = dfs(G1[rt1][i],G2[rt2][j]);
     			if(x!=-1) e[0].push_back(i),e[1].push_back(j+s1),e[2].push_back(x);
     		}
     	}
     	ans.init(s1,s2);
     	ll cost = 0;
     	for(int i=0;i<e[0].size();i++) ans.addEdge(e[0][i],e[1][i],1,e[2][i]);
     	int num = ans.MincostMaxflow(s1+s2,s1+s2+1,cost);
     	// printf("s1=%d s2=%d rt1=%d rt2=%d
    ",s1,s2,rt1,rt2);
     	// printf("rt1=%d rt2=%d s1=%d s2=%d num=%d cost=%lld
    ",rt1,rt2,s1,s2,num,cost);
     	if(num!=s1) return -1; 
     	if(rt1!=rt2) cost++;
     	// printf("cost=%lld
    ",cost);
     	return cost;
     }
    
     int main(){
     	int n;
     	scanf("%d",&n);
     	for(int i=1,x;i<=n;i++) scanf("%d",&x),G1[x].push_back(i);
     	for(int i=1,x;i<=n;i++) scanf("%d",&x),G2[x].push_back(i);
     	ll ans = dfs(G1[0][0],G2[0][0]);
     	printf("%lld
    ", ans);
     	return 0;
     }
    
    
    
  • 相关阅读:
    Django models中的null和blank的区别
    3次登陆锁定与backend增删改查
    Python全栈考试(一)
    python第一天几个小游戏
    linux开发脚本自动部署及监控
    Linux awk&shell script
    linux正则表达式grep&sed
    linux网路IP.设定主机名.ssh .bash命令&通配符
    Linux:nginx(web服务),nfs服务+反向代理+负载均衡
    Linux内存dd,rpm,yum,软件安装
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/13492567.html
Copyright © 2020-2023  润新知