POj-3723 Conscription-最大权森林

版权声明:欢迎转载 https://blog.csdn.net/suntengnb/article/details/80028208

问题描述:
Windy has a country, and he wants to build an army to protect his country. He has picked up N girls and M boys and wants to collect them to be his soldiers. To collect a soldier without any privilege, he must pay 10000 RMB. There are some relationships between girls and boys and Windy can use these relationships to reduce his cost. If girl x and boy y have a relationship d and one of them has been collected, Windy can collect the other one with 10000-d RMB. Now given all the relationships between girls and boys, your assignment is to find the least amount of money Windy has to pay. Notice that only one relationship can be used when collecting one soldier.
AC代码:

struct edge
{
    int u;
    int v;
    int w;
};
edge e[100010];
int n,m,r;
int boss[20010];
bool cmp(const edge &a,const edge &b)
{
    return a.w < b.w;
}
int get_boss(int x)
{
    if(boss[x] == x)
        return x;
    else
        return boss[x] = get_boss(boss[x]);// TLE!不要忘记路径压缩
}
void merge(int x,int y)
{
    int bx = get_boss(x);
    int by = get_boss(y);
    if(bx != by)
        boss[by] = bx;
    return;
}
bool query(int x,int y)
{
    int bx = get_boss(x);
    int by = get_boss(y);
    return bx == by;
}
int kruskal()//最小生成森林
{
    int ans = 0;
    sort(e + 1,e + r + 1,cmp);//贪心排序
    for(int i = 1; i <= r; ++i)
    {
        if(query(e[i].u,e[i].v) == false)
        {
            merge(e[i].u,e[i].v);
            ans += e[i].w;
        }
    }
    return ans;
}
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        cin >> n >> m >> r;
        for(int i = 1; i <= n + m; ++i)//初始化并查集
            boss[i] = i;
        for(int i = 1; i <= r; ++i)
        {
            int x,y,d;
            scanf("%d%d%d",&x,&y,&d);
            e[i].u = x + 1;//下标从1开始
            e[i].v = y + n + 1;//顶点编号不能重合,下标从1开始
            e[i].w = -d;//取相反数
        }
        cout << 10000 * (n + m) + kruskal() << endl;
    }
    return 0;
}

解决方法:
《挑战》上的问法是:要求通过适当的征募顺序使得征募所有人的所需费用最小
在征募顺序这个问法上算是难住我了,只有已经征募的人的关系才能够使用,没毛病啊。
所需费用就是10000*(m+n) - 关系优惠
关系优惠需要求个最大值,关系优惠是若干边权和,由于征募某人时,某人只能动用和已经征募的人之中一个人的关系,不能动用多个关系,所以这就是求最大生成树。最大生成树可以通过将边权取反求最小生成树来求得。
越看这道题越像生成树,那么征募顺序怎么体现呢。可以将生成树看做有序树,根结点先征募,叶子结点后征募(生成树本身是无序树,将其认为是有序树也是等价的),这样就打消了我的疑虑,题目吻合到生成树问题上来了。
联想到图中的序关系,容易想到拓扑排序,拓扑序存在的必要条件是图中没有环,否则会产生矛盾。这道题征募顺序还是有要求的,所以给出的图必没有环。
但是也未必保证生成的是一棵树,于是在kruskal中省去cnt == n - 1的判断。
对时间顺序的先后作出等价的变换或是用模型良好地解释会让求解更顺利地进行。
注意顶点的起始下标是1还是0,这会影响到并查集

猜你喜欢

转载自blog.csdn.net/suntengnb/article/details/80028208