616

足球锦标赛(bull.c/cpp/pas/in/out)

Time Limit:1s Memory Limit:128MB

 

【题目描述】

贝西和她的朋友们在参加一年一度的足球锦标赛。FJ的任务是让这场锦标赛尽可能地好看。

一共有N支球队参加这场比赛,每支球队都有一个取值在1~2^30-1之间的整数编号。

足球锦标赛是一个淘汰赛制的比赛——每场比赛过后,FJ选择一支球队淘汰,淘汰了的球队将不能再参加比赛。

锦标赛在只有一支球队留下的时候就结束了。

FJ发现了一个神奇的规律:在任意一场比赛中,这场比赛的得分是参加比赛两队的编号的异或(Xor)值。例如:编号为12的队伍和编号为20的队伍之间的比赛的得分是24分,因为 12(01100) Xor 20(10100) = 24(11000)。

FJ相信比赛的得分越高,比赛就越好看,因此,他希望安排一个比赛顺序,使得所有比赛的得分和最高。请帮助FJ决定比赛的顺序。

【数据输入】

第一行一个正整数N,代表球队的数量。

接下来N行,第i行1个正整数,代表第i支球队的编号。

【数据输出】

一行一个非负整数,表示所有比赛得分之和的最大值。

【样例输入】

4

3

6

9

10

【样例输出】

37

【数据范围】

对于30%的数据,n<=5

对于60%的数据,n<=100

对于100%的数据,n<=2000


emmmmmmm, 这道题当时用一个错误的算法, 只得到了10分, 知道正确的算法后依然改了40多分钟, 容易发现这个题就是求个最大生成树,两点间的边权等于编号的异或值,用Kruskal或者Prim算法都能通过此题(吧……), 显然法可以证明, 比较有意义, 所以记录下来, (其实被某人强迫的...)

 

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 4e6 + 100;
const int MAXM = 3e3 + 10;
const double eps = 1e-5;

template < typename T > inline void read(T &x) {
    x = 0; T ff = 1, ch = getchar();
    while(!isdigit(ch)) {
        if(ch == '-') ff = -1;
        ch = getchar();
    }
    while(isdigit(ch)) {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= ff;
}

template < typename T > inline void write(T x) {
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) write(x / 10);
    putchar(x % 10 + '0');
}

int n, tot = 0, fa[MAXM];
ll a[MAXM];

struct tree {
    int u, v;
    ll w;
}t[MAXN];

inline int get(int x) {
    return x == fa[x] ? x : fa[x] = get(fa[x]); 
}

inline bool cmp(tree x, tree y) {
    return x.w > y.w;
}

ll Kruskal() {
    ll sum = 0, cnt = 0;
    sort(t + 1, t + tot + 1, cmp);
    for(int i = 1; i <= n; ++i) fa[i] = i;
    for(int i = 1; i <= tot && cnt < n - 1; ++i) {
        int u = get(t[i].u), v = get(t[i].v);
        if(u != v) {
            ++cnt;
            sum += t[i].w;
            fa[u] = v;
        }
    }
    return sum;
}

int main() {
    freopen("bull.in", "r", stdin);
    freopen("bull.out", "w", stdout);
    read(n);
    for(int i = 1; i <= n; ++i) {
        read(a[i]);
    }
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= n; ++j) {/**/
            t[++tot].u = i;
            t[tot].v = j;
            t[tot].w = a[i] ^ a[j];            
        }
    }
    write(Kruskal());
    return 0;
} 

 

转载于:https://www.cnblogs.com/AK-ls/p/11039263.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值