HDU 6141 ( I am your Father! ) (最小树形图+权值编码)

来源:互联网 发布:淘宝店铺参加聚划算 编辑:程序博客网 时间:2024/06/05 15:57

I am your Father!

Problem Description
> Darth Vader: "Obi-Wan never told you what happened to your father."
>
> Luke Skywalker: "He told me enough! He told me you killed him!"
>
> Darth Vader: "No, I am your father."
>
> — Vader and Luke, on Cloud City

A list of n force-aware males numbered 1 through n were found. They are the chosen ones that will bring balance to the force. Being listed at the first place, Anakin Skywalker is the ancestor of all rest n1 persons.

Interestingly, everyone else claims he is the father of some others, causing serious troubles. Fortunately, you found the list also comes with these claims and their likelihood. Your task is to find the true father of Nikana, the last one in the list (numbered n).

There are m claims in the list. The i-th claim consists of three integers: xiyi, and wi, indicating that the xi-th person claims he is the father of the yi-th person, with a likelihood of wi.

Your task is to find a global assignment that assigns each person (except Anakin Skywalker) to someone in the list, i.e., find f(u) such that:

1. Everyone is assigned a father, i.e., f(u){1,2,,n} for all u{2,3,,n}.
2. Each one's assigned father claims their relationship, i.e., for all u, there exists a claim i in the claims such that f(u)=xiu=yi.
3. Nobody is an ancestor of himself in the assignment, directly or indirectly.
4. The assignment maximizes the sum of the likelihood of the father-and-son relationships, i.e., W=iwi if f(u)=xiu=yi is in the assignment.

You should find the father of Nikana (the person numbered n) in such an optimized assignment. If multiple assignments have the same optimal likelihood W, you should find the assignment that minimizes the lexical number of his father, i.e., minimizes f(n) at the same time has an optimal assignment likelihood W. That makes Nikana closer to Anakin Skywalker.
 

Input
There are multiple test cases in the input file. The first line of the input gives the number of test cases T, then followed by T test cases.

The first line of a test case contains n (1n103) and m (m104), the number of persons and the number of claims, respectively.

Then m lines follows. The i-th line contains three integers: xiyi, and wi indicating the claimed father, son, and likelihood. 1wi100 is guaranteed. Nobody will claim someone as his son twice.
 

Output
For each test case, output one line containing two space-separated integers, the maximum likelihood W and the father of Nikana.
 

Sample Input
23 31 2 101 3 102 3 103 31 2 101 3 102 3 11
 

Sample Output
20 121 2
 

Source
2017 Multi-University Training Contest - Team 8
 

Recommend
liuyiding   |   We have carefully selected several similar problems for you:  6143 6142 6141 6140 6139 

题意:给定一个有向图,求以1为根节点的最大树形图是多少并且输出n号节点的父亲节点(父亲节点字典序需要最小)。n是节点数目,m是边的数目。

分析:首先,这里求的是最大树形图,我们可以将所有边的权值乘以-1,然后根据最小树形图算法,求出最小树形图的权值和,再乘回-1就是该有向图的最大树形图权值。但是这样是求不出n号节点的最小字典序父亲节点的,朱刘算法中会将节点序号打乱,也就是我们会丢失节点序号,那这里怎么办呢?这里就用到了权值编码。我们可以思考,既然朱刘算法会将节点序号改变,那么什么是不变的呢?那肯定就是进入n号节点的最小边,如果我们将n号节点的父亲节点信息存到边中,然后再还原回来,不就可以了?这就是权值编码的神奇之处了。这里的操作是,我们将所有的权值都乘以-n,为什么是乘以-n,而不是乘以别的数呢?这里做个记号#1,先不讨论。然后当存在某条边(u,v,w),其中v是n号节点,那么我就将这条边的权值w+=u(这里的w已经进行过乘以-n的操作了。),那么这里问题来了,我这样w+=u,会不会改变节点到达n点权值的相对大小呢?正常情况下是会的,但是这里我们可以提前避开这个问题,这里做个记号#2,先不讨论。然后,当我们根据以上操作处理完所有的权值以及到达n的权值之后,就可以直接跑最小树形图。当我们做完最小树形图之后,会得到一个负数ans1,这个ans1=-n*wi+u,其中wi表示选中边初始的权值和,u表示进入n的父亲节点(神奇吧?),那么此时我们经过乘以ans1*-1,会得到ans2=n*wi-u,这时,我们可以很明显的发现,我们所求的最大树形图权值就是所有wi的和,n的最小字典序父亲节点就是u。到了这一步,我们的工作就是处理ans2。对于最大树形图的权值,我们不能直接(ans2=n*wi-u)/n,这里-u的做操作会使最终结果少1,因此,我们这样操作(ans2+n-1)/n=(n*wi-u+n-1)/n,这里0<-u+n-1<n,因此最大树形图的权值cost=(ans2+n-1)/n,求出,权值,我们需要将u提取出来。根据上边的结论,ans1=-n*wi+u,然后cost=wi,那么我们可以很快得出u=cost*n+ans1,然后事情就完美解决啦~PS:注意这里的wi都是指选中边的初始权值和而不是一条边

这里来解释一下#1和#2操作。首先,#2出现的问题是在#1的基础上完美避开的,在#2中,如果两条边的权值w(负数)相同,我们通过加上u值,这里假设u1<u2,那么w+u1<w+u2,然后u1显然是我们需要找的父亲节点,w+u1这条边也就是我们要找的。当两条边的权值w1,w2(已经乘-n变成负数)不同的时候,对于u1,u2,必然有w1+u1和为w2+u2,其中必然存在0<|u1-u2|<n,此时我们假设w1<w2,那么显然w1比w2更优,那么我们如何使w1+u1<w2+u2成立呢??此时我们知道w1!=w2,那么必然有|w2-w1|≥n,这里我们的w1和w2已经扩大了-n倍,那么也就是说,在w1<w2的情况下,必然存在w2-w1≥n,那么此时证明不等式w2+u2>w1+u1等价于证明w2-w1>u1-u2,由于w2-w1≥n,n>|u1-u2|,
也就是w2-w1>u1-u2在(权值*-n)的情况下始终成立,也就是我们不会丢失最优解!!也就是说,只要我们乘以比-n小的任何数,都可以保持最优解不变。然后这道题就解决了。神奇的权值编码,最小树形图压根不会呀T_T~~还要刷最小树形图~~~~~~

AC代码:
#include<cstdio>#include<cstring>#include<cmath>using namespace std;struct Edge{    int u,v;    int w;};Edge edge[10005];const int INF=0X3F3F3F3F;int pre[1005], id[1005], vis[1005];int in[1005];double Dir_MST(int root, int V, int E){    double ret = 0;    while(true)    {        //1.找最小入边        for(int i = 0; i < V; i++) in[i] = INF;        for(int i = 0; i < E; i++)        {            int u = edge[i].u;            int v = edge[i].v;            if(edge[i].w < in[v] && u != v) {pre[v] = u; in[v] = edge[i].w;}        }        for(int i = 0; i < V; i++)        {            if(i == root) continue;            if(in[i] == INF) return -1;//除了跟以外有点没有入边,则根无法到达它        }        //2.找环        int cnt = 0;        memset(id, -1, sizeof(id));        memset(vis, -1, sizeof(vis));        in[root] = 0;        for(int i = 0; i < V; i++) //标记每个环        {            ret += in[i];            int v = i;            while(vis[v] != i && id[v] == -1 && v != root)  //每个点寻找其前序点,要么最终寻找至根部,要么找到一个环            {                vis[v] = i;                v = pre[v];            }            if(v != root && id[v] == -1)//缩点            {                for(int u = pre[v]; u != v; u = pre[u]) id[u] = cnt;                id[v] = cnt++;            }        }        if(cnt == 0) break; //无环   则break        for(int i = 0; i < V; i++)            if(id[i] == -1) id[i] = cnt++;              //3.建立新图        for(int i = 0; i < E; i++)        {            int u = edge[i].u;            int v = edge[i].v;            edge[i].u = id[u];            edge[i].v = id[v];            if(id[u] != id[v]) edge[i].w -= in[v];        }        V = cnt;        root = id[root];    }    return ret;}int main(){    int n,m;    int N;    /*freopen("in.txt","r",stdin);    freopen("1001.txt","w",stdout);*/    scanf("%d",&N);    while(N--){        scanf("%d%d",&n,&m);        for(int i = 0; i < m; i++)        {            scanf("%d%d%d", &edge[i].u, &edge[i].v,&edge[i].w);            edge[i].u--;            edge[i].v--;            edge[i].w*=-n;            if(edge[i].v==n-1){                edge[i].w+=edge[i].u;            }        }        int ans=Dir_MST(0,n,m);        printf("%d %d\n",(n-1-ans)/n,(-ans+n-1)/n*n+ans+1);    }    return 0;}



原创粉丝点击