Battle Over Cities - Hard Version (35 分)

最近不知道怎么养成了喜欢看知乎的习惯,每天到实验室都会刷一会儿知乎,看一些我也不知道看的目的在哪里的话题,而且还津津有味,哈哈哈…大概只要不学习,时间总过得很快。认真的想一想,我还有什么日常碎碎念,现在感觉自己学习吧,效率很低,大概主观能动性不强,还需要日常push估计才能好好学习吧。
OK,日常闲聊一份钟结束,上一篇博客是Battle Over Cities,这一次是升级版,也是鬼鬼珊第一次去做Top Level的题,嘿嘿,其实前天也看了其他两道题,但是看得我脑壳痛,瞬间浇灭了我燃起的熊熊焰火。fine,切换频道…

It is vitally important to have all the cities connected by highways in a war. If a city is conquered by the enemy, all the highways from/toward that city will be closed. To keep the rest of the cities connected, we must repair some highways with the minimum cost. On the other hand, if losing a city will cost us too much to rebuild the connection, we must pay more attention to that city.

Given the map of cities which have all the destroyed and remaining highways marked, you are supposed to point out the city to which we must pay the most attention.

Input Specification:
Each input file contains one test case. Each case starts with a line containing 2 numbers N (≤500), and M, which are the total number of cities, and the number of highways, respectively. Then M lines follow, each describes a highway by 4 integers: City1 City2 Cost Status where City1 and City2 are the numbers of the cities the highway connects (the cities are numbered from 1 to N), Cost is the effort taken to repair that highway if necessary, and Status is either 0, meaning that highway is destroyed, or 1, meaning that highway is in use.

Note: It is guaranteed that the whole country was connected before the war.

Output Specification:
For each test case, just print in a line the city we must protest the most, that is, it will take us the maximum effort to rebuild the connection if that city is conquered by the enemy.

In case there is more than one city to be printed, output them in increasing order of the city numbers, separated by one space, but no extra space at the end of the line. In case there is no need to repair any highway at all, simply output 0.

Sample Input 1:
4 5
1 2 1 1
1 3 1 1
2 3 1 0
2 4 1 1
3 4 1 0
Sample Output 1:
1 2
Sample Input 2:
4 5
1 2 1 1
1 3 1 1
2 3 1 0
2 4 1 1
3 4 2 1
Sample Output 2:
0

  • 介绍:首先先解释一下这个题目在说什么,如果一个城市(就是图中的节点)被敌军占领,那么这个与这个节点相连的所有边都将被关闭,要使剩下的节点连通就需要修复一些已经被停用的边,修复这些边要花钱吧。不同节点被占领之后,花费的钱也是不一样的。简要概括一下,就是求出每一个节点被占领之后,要使剩下的节点连通要花多少钱,然后输出花费最多的节点。
  • 思路:
    1、一个节点删除之后,求剩余节点连通的最小花费,即最小生成树,Prim和Kruskal算法皆可。
    2、我使用的是Prim算法,关于这个算法的详情,我找了一篇博客介绍,Prim算法介绍
    3、作为一个小白来说,我在做的时候,出现了一个问题,我的lowcost这个数组在整个算法的赋值,因为Prim算法每次都是从这个数组中选取最小的值所对应的节点加入到当前的树。我这里就介绍我是怎么做的,不做其他情况的假设。赋初值的时候,对于一条边,不管是否在使用,我都是赋初值为边的权重,这是因为我考虑一种情况,如果一个节点加入之后,和当前生成的树相连的边都没有使用,那我就可以去遍历这个数组找出最小的值。其实对于没有在用的边,很容易根据这个条件去想到。而对于还在使用的边,如何去赋lowcost的值才不容易想,按照我这种赋值方法,因为会存在一条在使用的边的权重比没有使用的边的权重要大,因此每一次迭代的时候,选取的时候,就会出现很多细节上的问题,需要慢慢的去调试。
    4、根据我上面的赋值方法,我最开始找到和closeset对应节点在用并且lowcost最小的节点进行加入,这个时候所对应的删除的点加入的总cost就为0。如果找不到,就在没有相连的中去找lowcost的最小的节点,所对应的删除的点加入的总cost就为最小lowcost的值。尤其注意就是,还可能存在就是就算修复了所有的路也没办法连通,即已经找不到点可以加入到当前的树了并且已经加入的点小于N-1,这个时候可以直接返回一个无穷大值
    5、选好了这一次迭代要加入的点index之后,就要对相应的lowcost和相应的closeset进行更改了,这里也要特别注意一个小点,首先是传统的节点的lowcost[i]和edge<v,i>.weight之间的值的大小进行比较,还有一种情况是,之前的lowcost[i]小于edge<index.i>.weight,但是这个还是应该修改相应的lowcost和closeset,其实从这里可以看出,对于在用的边,lowcost等于初始权重值也存在一点不合适的,但我的代码还没有进行优化,所以目前的版本就先贴这个吧。以后我优化之后会进行更改,也来一个code hard version,哈哈哈。
  • 讲的有点繁琐,但是真的要注意一个点就是文中加粗的地方,不然可能会导致有的测试用例不通过。

贴代码啦

#include <iostream>
#include<bits/stdc++.h>
#define MaxSize 501
const int INF=0x7fffffff;       //定义一个很大的数
using namespace std;
struct Edge{
   int cost;   //花费
   int use;   //是否可以使用
};
Edge Graph[MaxSize][MaxSize];     //存放图
int cost_conquered[MaxSize];    //表示该城市被占领会需要花费多少
int lowcost[MaxSize];      //存放该节点到最小生成树的花费
int closeset[MaxSize];      //存放最小花费和当前相连的节点
int join[MaxSize];          //表示这个点是否有道路相连
int N,M;      //分别表示总的城市个数和连接城市的边
void Input(){
    int temp_s,temp_d,temp_cost,temp_use;
    for(int i=0;i<M;i++){
        cin>>temp_s>>temp_d>>temp_cost>>temp_use;
        Graph[temp_s][temp_d].cost=temp_cost;
        Graph[temp_s][temp_d].use=temp_use;
        Graph[temp_d][temp_s].cost=temp_cost;
        Graph[temp_d][temp_s].use=temp_use;
    }
}
//这里注意,其实也不是真正意义上的最小生成树,如果没有相连的就找最小的边,连起来就直接加进去
void Prim(int v,int u){
    int Min,index;
    lowcost[v]=0;
    for(int i=1;i<=N;i++){
        if(lowcost[i]!=0)   {
                lowcost[i]=Graph[v][i].cost;
                closeset[i]=v;
        }
        if(Graph[v][i].use==1){
            join[i]=1;
        }
    }
    for(int i=1;i<=N-2;i++){     //找出剩下的节点
        Min=INF;
        index=-1;
        for(int j=1;j<=N;j++){
            if(lowcost[j]!=0&&lowcost[j]<Min&&Graph[closeset[j]][j].use==1){   //找到一个和当前节点相连并且这条边没有被破坏
                Min=lowcost[j];
                index=j;
            }
        }
        if(index==-1){   //如果找不到这个点,则说明当前的点都没办法连
            Min=INF;
            for(int j=1;j<=N;j++){
                 if(lowcost[j]!=0&&lowcost[j]<Min){
                    Min=lowcost[j];
                    index=j;
                 }
            }
            cost_conquered[u]+=Min;
        }
        if(index==-1){    //剩下的节点没办法连通
            cost_conquered[u]=INF;
            return;
        }
        lowcost[index]=0;
        for(int j=1;j<=N;j++){    //当前节点加入之后,修改相应的lowcost和closeset
            if(Graph[index][j].cost<lowcost[j]||(join[j]==0&&Graph[index][j].use==1&&lowcost[j]!=0)){
                join[j]=1;
                lowcost[j]=Graph[index][j].cost;
                closeset[j]=index;
            }
        }
    }
}
int main()
{
    int Max=0;
    memset(cost_conquered,0,sizeof(cost_conquered));
    int Count=0;
    cin>>N>>M;
    for(int i=1;i<=N;i++){
        for(int j=1;j<=N;j++){
            Graph[i][j].cost=INF;
            Graph[i][j].use=0;
        }
    }
    Input();
    for(int i=1;i<=N;i++){
        memset(join,0,sizeof(join));
        for(int j=1;j<=N;j++){
            lowcost[j]=INF;
            closeset[j]=-1;
        }
        lowcost[i]=0;   //表示这个点已经排除
        if(i==1){
            Prim(2,1);    //当考虑第一个节点,那么第一个加入的节点就不能是1
        }
        else{
            Prim(1,i);    //当考虑剩下的节点时,那么首节点只要不和自己相同即可,所以选择了1
        }
        if(cost_conquered[i]>Max){
            Max=cost_conquered[i];
        }
    }
    if(Max==0){
        cout<<'0';
    }
    else{
        for(int i=1;i<=N;i++){
            if(cost_conquered[i]==Max){
               Count++;
            }
        }
        int i=1;
        while(Count>1){
            if(cost_conquered[i]==Max){
                cout<<i<<" ";
                Count--;

            }
            i++;
        }
        for(;i<=N;i++){
            if(cost_conquered[i]==Max){
                cout<<i;
            }
        }
    }
    return 0;
}

测试结果

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值