XDOJ1004 亚特兰提斯

 

描述:

  为了找寻沉睡的亚特兰提斯大陆,wm来到了大西洋上进行探险,找了半个月仍一无所获。然而在一次突袭而来的暴风雨后,wm的船莫名地驶入了一片未知的区域,发现了一个地图上未标记的岛屿,这让他感到相当惊讶,强烈的好奇心驱使他上岸看看究竟。
船在岛屿靠岸后岛上的国王举行庆典热情地款待了他,整个岛一片喜庆。国王自称是亚特兰提斯人,而这个岛屿曾经是亚特兰提斯大陆的最高地。现在岛屿上还有N个城市,由于岛上的资源相当有限且岛上没人知道怎么合理利用资源故一直以来在城市之间都没有铺设道路,现在国王想请wm规划下道路并进行建设,国王对规划的要求如下:
  1.所有城市必须连通,即任意两个城市都可以互相来往
  2.国王是个娇气的人,所以他希望从任意城市走到任意另一个城市时的“舒适度”越高越好,其中两个城市间的“舒适度”定义为:两个城市间路径的每一段道路的“舒适度”的最小者,例如从A到B需要经过C城市,A->C段“舒适度”为10,C->B段“舒适度”为100,则A->B的“舒适度”为10(当然如果两个城市间有很多路径的话国王会走“舒适度”最大的路径)。现在定义K为所有两个城市间“舒适度”的最小者,而国王希望K越大越好。
  现在岛上的资源只有R单位,国王希望wm帮他规划道路满足他的要求,在岛上资源条件的允许下输出最大的K。如果岛上的资源不够满足第1个要求,则输出-1。
wm沉迷于岛上的玩乐,懒得去想这问题了,所以他远程求助你,帮他解决这个烦人的问题。

输入:

  输入包含多组数据,EOF结束。
  每组数据以三个整数开头,N,M,R。N(2<=N<=1000)是城市数,M(0<=M<=100000)是允许建造的道路数,R(0<=R<=100000)是岛上仅有的资源数。EOF结束。
  接下来有M行数据,每行包括a,b(0<=a,b<N),v(0<v<=100000),c(0<=c<=100000)四个整数,表示ab城市间允许建一条双向道路,花费v单位资源,“舒适度”为c。

输出:

  每组数据输出一行,岛上资源条件的允许下最大的K或-1。

样例输入:

3 3 1000
0 1 20 8
1 2 10 7
2 0 10 6
3 3 20
0 1 20 8
1 2 10 7
2 0 10 6
2 1 10
0 1 20 8

样例输出:

7
6
-1

提示:

  第一组样例资源允许建设全部三条路(当然也可以只建0-1和1-2这两条道路),其中0-1之间的“舒适度”为8,1-2之间的“舒适度”为7,0-2之间的“舒适度”为7(可以从城市1中转,不直接走0-2这条“舒适度”为6的道路),故K值为min(8,7,7)=7,由于找不到更好的方案使K值更大,故输出7。

 

解题思路:

看到这个题的第一个想法是想先分析题意。

符号说明:

设需要建设的道路集为S。

对S中的每条道路road(k),其舒适度设为C(k),成本设为W(k)

目标:       Max(Min{C(k)})

约束:

1.S集中的道路需要能保证两两城市相通   

2. sum(W(k))<=R,R是成本限制

 

如果目标是求总成本最小,那就是MST问题。但显然不是这样,之后我就想这和MST有什么关系呢?MST是满足约束1和2的解,目标就是设法使MST树中边的最小舒适度最大。所以我想到下面这个解法:

(1)对所有路按成本从小到大排序,生成最小生成树,得到树的最小舒适度C1

(2)如果无法生成MST或成本合大于R,输出 -1.结束

(3)去掉舒适度小于等于C1的所有路,再生成MST树,得到树的最小舒适度C2

(4)如果无法生成MST树或成本大于R,输出C1,结束。否则C1=C2, 转向(3)

实现算法后,程序一直不能AC,显示超时,所以又在网上找答案,一个更快的算法如下:

(1)对所路按成本从小到大排序

(2)以二分查找的方式从1~100000之间找满足条件1,2的最小生成树,找到的值就是所要的舒适度。

 

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;


struct Road{
    int start;
    int end;
    int cost;
    int comfor;
};

vector<Road> roads;
vector<int> costOrd;
vector<int> parent;
vector<int>::iterator iter;
int N,M,R;
bool lessCost(const int &r1,const int &r2)
{
    return roads.at(r1).cost<roads.at(r2).cost;
}
int find_root(int x)
{
    while(x!=parent.at(x))
        x = parent.at(x);
    return x;
}
bool krusker(int curComfor)
{
    int sumCost = 0;
    parent.clear();
    for(int i=0;i<N;++i)
    {
        parent.push_back(i);
    }
    int count = 0;
    for(iter=costOrd.begin();iter!=costOrd.end();++iter)
    {
        if(roads.at(*iter).comfor<curComfor)
            continue;
        int p1 = find_root(roads.at(*iter).start);
        int p2 = find_root(roads.at(*iter).end);
        if(p1!=p2)
        {
            sumCost += roads.at(*iter).cost;
            parent.at(p1) = p2;
            if(sumCost>R)
                return false;
            ++count;
            if(count==N-1)
                return true;
        }
    }
    return false;
}
int main()
{
    while(cin>>N>>M>>R)
    {
        roads.clear();
        costOrd.clear();
        for(int i=0;i<M;++i)
        {
            Road path ;
            cin>>path.start>>path.end>>path.cost>>path.comfor;
            roads.push_back(path);
            costOrd.push_back(i);
        }
        sort(costOrd.begin(),costOrd.end(),lessCost);

        int left = -1;
        int right = 100001;
        int mid;
        while(left<right-1)
        {
            mid = (left+right)/2;
            if(krusker(mid))
                left = mid;
            else
                right = mid;
        }
        cout<<left<<endl;
    }

    return 0;
}

 

 

最后欢迎大家访问我的个人网站: 1024s

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值