CSUOJ 1116 Kingdoms

题目链接:http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1116、

1116: Kingdoms

Time Limit: 3 Sec   Memory Limit: 64 MB
Submit: 441   Solved: 145

Description

A kingdom has n cities numbered 1 to n, and some bidirectional roads connecting cities. The capital is always city 1.
After a war, all the roads of the kingdom are destroyed. The king wants to rebuild some of the roads to connect the cities, but unfortunately, the kingdom is running out of money. The total cost of rebuilding roads should not exceed K.
Given the list of m roads that can be rebuilt (other roads are severely damaged and cannot be rebuilt), the king decided to maximize the total population in the capital and all other cities that are connected (directly or indirectly) with the capital (we call it "accessible population"), can you help him?

Input

The first line of input contains a single integer T (T<=20), the number of test cases. 
Each test case begins with three integers n(4<=n<=16), m(1<=m<=100) and K(1<=K<=100,000). 
The second line contains n positive integers pi (1<=pi<=10,000), the population of each city. 
Each of the following m lines contains three positive integers u, v, c (1<=u,v<=n, 1<=c<=1000), representing a destroyed road connecting city u and v, whose rebuilding cost is c. 
Note that two cities can be directly connected by more than one road, but a road cannot directly connect a city and itself.

Output

For each test case, print the maximal accessible population.

Sample Input

2
4 6 6
500 400 300 200
1 2 4
1 3 3
1 4 2
4 3 5
2 4 6
3 2 7
4 6 5
500 400 300 200
1 2 4
1 3 3
1 4 2
4 3 5
2 4 6
3 2 7

Sample Output

1100
1000

HINT

Source


题目大意:

有编号1到n的n个城市和m条道路,编号为1的城市为首都,每座城市有Pi(1 <= i <= n)的人口,一共有K的预算,修复每条道路来连接城市u和v需要c的费用,在总费用不超过预算的前提下,要求让城市与首都连通使这些与首都连通的城市(包括首都)的人口尽可能的多。

解题思路:

很明显的看出有个最小生成树,但是还要求人口最多(不一定最后所有城市都可以与首都连通),状态压缩dp,n的范围不超过16,用int的二进制,二进制的2^x位为1表示选择第x + 1个城市来构造最小生成树。

#include <iostream>
#include <algorithm>
#include <cstdio>

using namespace std;

struct Edge{
    Edge(int x = 0, int y = 0, int Weight = 0) : x(x), y(y), Weight(Weight){
    }

    int x;
    int y;
    int Weight;

    bool operator < (const Edge& b) const{
        if(this->Weight < b.Weight) return true;
            else if(this->Weight == b.Weight){
                if(this->x < b.x) return true;
                    else if(this->x == b.x) return this->y < b.y;
                        else return false;
                }else return false;
    }
};

void ReSet(int arr[], int n){
    for(int i = 1; i <= n; i++) arr[i] = i;
}

int FindFather(int arr[], int x){
    if(arr[x] == x) return x;
    return arr[x] = FindFather(arr, arr[x]);
}

int Merge(int arr[], int a, int b){
    int fa = FindFather(arr, a),
        fb = FindFather(arr, b);

    if(fa != fb) return arr[fb] = arr[fa];
    return -1;
}

int T, n, m, K, u, v, c, cost, sum, ans, x, y;
int p[17], F[17];
Edge E[101];

int main()
{
    scanf("%d", &T);
    for(int t = 1; t <= T; t++)
    {
        scanf("%d%d%d", &n, &m, &K);
        for(int i = 1; i <= n; i++) scanf("%d", &p[i]);
        for(int i = 0; i < m; i++){
            scanf("%d%d%d", &u, &v, &c);
            E[i] = Edge(u, v, c);
        }
        sort(E, E + m);  //对边按权值从小到大排序
        ans  = 0;
        for(int i = 3; i <= (1 << n) - 1; i++){
            if((i & 1) == 0) continue;  //去掉不含首都的状态
            ReSet(F, n);
            sum  = p[1];
            cost = 0;
            for(int j = 0; j < m; j++){    //最小生成树
                if(((1 << (E[j].x - 1)) & i)
                   && ((1 << (E[j].y - 1)) & i)
                   && FindFather(F,E[j].x) != FindFather(F, E[j].y)){
                    Merge(F, E[j].x, E[j].y);
                    cost += E[j].Weight;
                   }
            }
            for(int j = 2; j <= n; j++){  //判断选中的城市是否都与首都连通
                if(((1 << (j - 1)) & i) && FindFather(F, j) != FindFather(F, 1)){cost = 100001; break;}
            }
            if(cost <= K){  //花费不超过预算且所有被选中的城市都与首都连通则计算该状态下的人口
                for(int j = 2; j <= n; j++)
                    if((1 << (j - 1)) & i) sum += p[j];
                if(sum > ans) ans = sum;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值