【HDU4313】Matrix 多校 解题报告+AC代码+思路+算法正确性证明,此为Kruskal贪心简单版本,恶心版本稍后放出【目标达成 0.2%】

5 篇文章 0 订阅
4 篇文章 0 订阅
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>

using namespace std;
/**
    c0de4fun声明:本人未给出测试数据、未声明一次AC的题均为参考解题报告自己写的。
                  对那些无私贡献的大神真诚的致敬!本题的测试数据可以看最后面
    Problem: HDU4313 - Matrix
    Reference:
    I-   http://page.renren.com/601081183/note/862977450?ref=minifeed&sfet=2012&fin=5&ff_id=601081183&feed=page_blog&tagid=862977450&statID=&level=
         好吧这个网址太尼玛蛋疼了!如果想看的话,人人网搜索HDOJ
         找到2012 Multi-University Training Contest 2 Solution Report
         1004-Matrix就是本题的思路。
    II-  http://www.2cto.com/kf/201207/144028.html
         ↑参考代码
         来源居然是红黑联盟……
         很多年前了吧,现在也洗白了。
         祝他们发展的越来越好
    Knowledge Point: Kruskal+贪心算法  【或】 树形DP,本版本是Kruskal的,还会写树形dp
    Version: Kruskal贪心简单版
    Thought:

    解题思路:
        并查集+贪心
        思路:
        我们把有机器的节点颜色染成黑色(看到这句话别怕。。。其实跟什么红黑树一点关系都没有,就是为了方便)
        那么,如果一个集合中有黑色节点的话,他就不能再跟有黑色节点的集合连接,也不能再连接黑色节点。
        我们判断代表节点的颜色,然后按照Kruskal生成树,如果碰到集合代表是黑色节点,并且将与黑色节点连接或者
        与另一个代表节点为黑色的集合连接,我们就干掉这条边,并且费用增加对应的权值。



    一、关于采用Kruskal贪心算法正确性的证明。
    【注意,我们这里关于有些术语的定义跟《算法导论》中的不一样!请仔细阅读!】
    【注意,我们在使用该贪心算法之前已经将边按照《权值由高到低》排序了,阅读时请务必记住这一点】
    前提:
    给定的树是一颗。。。那叫什么树啊,也不叫最小生成树,反正就是能让全图连起来的树咯。
    定义:
    【安全边】:加入该边之后不会使两台机器连起来的边。
    1.初始化:
    对于第一条加入树的边(i,j),有三种情况:
        I.color[i]与color[j]都是白色——(i,j)安全边,加入树
        II.color[i]与color[j]一黑一白——(i,j)安全边,加入树
        III.color[i]与color[j]都是黑色——危险边,删除并计算费用!( sum = sum + weight(i,j) )
    由于我们将边按照权值由高到低排列了,那么无论是以上三种情况的哪一种,sum都是最优的。
    2.保持
    对于一个安全边集合(就是已经确定了一棵树,但是该树未遍历完所有的边,亦即还有一些边没有加入到树中)
    ,新加入该集合的边(i,j)与I其实一样的,分1.中的三种情况。
    那么,【由于边按照权值从高到低排序】,已经加入这棵树中的所有边都是安全边,我们遇到的要加入这棵树的第一条危险边就是费用最小的、让机器联通起来的边了。
    因为权值大的安全边都被我们尽可能的塞进了这棵树。
    那么此时费用sum也是最优的。
    如果这么说想不明白的话,那么就把这个安全边的集合想象成一个新的节点i,j是即将跟它连起来的节点!
    3.终止
    这还终止个毛啊,根据1和2遍历完给定图中所有的边之后,我们得到的必然是最小费用,直接输出就好。



    二、并查集代表节点颜色问题(这部分在这个代码中没有体现,不给出证明了,在恶心代码中会给出证明)


*/
const int WHITE = 0;
const int BLACK = 1;
const int MAX_SIZE = 100010;

struct node
{
    int id;
    int x;
    int y;
    int val;
};
node nodes[MAX_SIZE];
int fa[MAX_SIZE]; //娘希匹的,以后并查集强迫自己用int数组写,否则写到struct里太非主流
int color[MAX_SIZE]; //节点颜色
int rank[MAX_SIZE];

int comp (const void *a,const void *b)
{
    node *_a = (node*)a;
    node *_b = (node*)b;
    return _b->val - _a->val; //sort by val,order by decrease.
}

int findFather(int id)
{
    int x = id;
    while ( fa[x] != x )
    {
        fa[x] = fa[ fa[x] ];
        x = fa[x];
    } //这么写并查集不会超过三层。
    return x;
}
bool join(int x,int y)
{
    int fax = findFather(x);
    int fay = findFather(y);
    if( (fax != fay ) && (color[fax] == BLACK && color[fay] == BLACK ) )
    {
        return false;
    }
    if( color[fax] == BLACK )
    {
        fa[fay] = fax;
        rank[fax] = rank[fax] + rank[fay];
    }
   // if( color[fay] == BLACK )
    else
    {
        fa[fax] = fay;
        rank[fay] = rank[fay] + rank[fax];
    }
    return true;
}
long long solve(int N)
{
    long long res = 0;
    for(int i = 0 ; i < N-1 ;i++)
    {
        if ( join(nodes[i].x,nodes[i].y) == 0 )
        {
//#define DBG
#ifdef DBG
            printf("double black cuted : %d %d val is %d\n",nodes[i].x,nodes[i].y,nodes[i].val);
#endif
            //join faild. Two black node has been linked! deleted.
            res = res + nodes[i].val;
        }
    }
    return res;
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("B:\\acm\\SummerVacation\\DP-II\\C.in","r",stdin);
    freopen("B:\\acm\\SummerVacation\\DP-II\\C.out","w",stdout);
#endif
    int N,K,T;
    while(scanf("%d",&T) != EOF)
    {
        for(int t = 1 ; t <= T; t++)
        {
            memset(fa,0,sizeof(fa));
            memset(color,0,sizeof(color));
            memset(nodes,0,sizeof(nodes));//memset是TLE和MLE杀手,不过据说是初始化大数组最快的,╮(╯_╰)╭谁知道呢
            scanf("%d%d",&N,&K); //initallization over.
            for(int i = 0 ; i < N-1 ; i++)
            {
                scanf("%d%d%d",&nodes[i].x,&nodes[i].y,&nodes[i].val);
                nodes[i].id = i;
                //fa[i] = i;  //Its father is itself.
               // rank[i] = 1;
               // color[i] = WHITE; // No machine.
               fa[nodes[i].x] = nodes[i].x;
               fa[nodes[i].y] = nodes[i].y;
               rank[nodes[i].x] = 1;
               rank[nodes[i].y] = 1;
               color[nodes[i].x] = WHITE;
               color[nodes[i].y] = WHITE;
            }

            for(int i = 0 ; i < K ; i++)
            {
                int nid;
                scanf("%d",&nid);
                color[nid] = BLACK; //set it have a machine.
            }
            qsort(nodes,N-1,sizeof(nodes[0]),comp);
            long long  ans = solve(N);
            printf("%I64d\n",ans);
        }

    }
#ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
#endif
    return 0;
}
2
5 3
2 1 8
1 0 5
2 4 5
1 3 4
2
4
0
14 4
0 1 1
0 4 4
1 3 3
2 1 2
5 4 5
5 8 7
5 7 8
6 5 6
9 6 1
9 10 9
12 9 10
11 9 11
13 11 12
11
5
3
1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值