HDU-3832.Earth Hour

该博客探讨了一道图论问题,即在确保前三个路灯相互连接的情况下,计算最多可以关闭多少路灯。通过将路灯的覆盖范围相交视为相连,转换为寻找中转路灯的最短路径问题。利用SPFA或Dijkstra算法求解单源最短路径,找到连接三个路灯的最短距离之和最小的方案,从而得出最少需要开启的路灯数量。文章提供了两种算法的实现,一种是SPFA,另一种是Dijkstra,并给出了详细的代码示例。
摘要由CSDN通过智能技术生成

3832.Earth Hour

标签:图论,最短路

题意:给定n个路灯的坐标和照射半径,当两个路灯的照射范围相交时,这两个路灯视为相连,现在问在保证前三个路灯相连的情况下,最多可以关闭多少路灯,若无法相连,则输出-1

题解

题目要求尽可能多地关闭路灯,实际上也就是要求尽可能少地打开路灯使得前三个路灯相连,如果前三个路灯相连,那么肯定存在一个中转路灯能连接这三个路灯,而相连我们可以用连边来表示,所以问题可以转化为前三个路灯到这个中转路灯的最短距离,这个最短距离我们可以分别以前三个路灯为源点,用spfa或dij求单源最短路径,最后遍历这个中转路灯,三个最短距离的和 + 1的最小值就是最少需要打开的路灯,那么答案用n减去这个结果即可

需要注意的是,如果你没有开longlong,在对距离数组赋初值的时候,不能赋值成接近1e9这么大的数(虽然平常习惯如此),这是因为本题中会出现三个距离数组相加的情况,这样某些情况加起来会爆int

代码实现

spfa

#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <iomanip>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <math.h>
#define ll long long
using namespace std;
int t, n;
struct node{
   int x, y, r; 
}e[1010];
int d[1010][1010], dis[4][1010], vis[1010];
void spfa(int s){
    memset(vis, 0, sizeof(vis));
    for(int i = 1; i <= n; i++) dis[s][i] = 1e6;
    dis[s][s] = 0;
    vis[s] = 1;
    queue<int> q;
    q.push(s);
    while(!q.empty()){
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = 1; i <= n; i++){
            if(dis[s][u] + d[u][i] < dis[s][i]){
                dis[s][i] = dis[s][u] + d[u][i];
                if(!vis[i]){
                    vis[i] = 1;
                    q.push(i);
                }
            }
        }
    }
}
int main(){
    cin >> t;
    while(t--){
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> e[i].x >> e[i].y >> e[i].r;
        for(int i = 1; i <= n; i++){
            for(int j = 1; j <= n; j++){
                double tmp = sqrt((double)(e[i].x - e[j].x) * (e[i].x - e[j].x) + (e[i].y - e[j].y) * (e[i].y - e[j].y));
                if(tmp <= e[i].r + e[j].r) d[i][j] = 1;
                else d[i][j] = 1e6;
                d[i][i] = 0;
            }
        }
        spfa(1);
        spfa(2);
        spfa(3);
        int ans = 1e6, f = 0;
        if(dis[1][2] == 1e6 || dis[1][3] == 1e6 || dis[2][3] == 1e6) f = 1;
        if(f) cout << -1 << "\n";
        else{
            for(int i = 1; i <= n; i++) ans = min(ans, dis[1][i] + dis[2][i] + dis[3][i]);
            cout << n - ans - 1 << "\n";
        }
    }
    return 0;
}

dij

#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <iomanip>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <math.h>
#define ll long long
using namespace std;
const int N = 40050;
int t, n, cnt;
int now[N];
struct node{
    int x, y, r;
}e[210];
struct Node{
    int u, w;
};
bool operator <(Node x, Node y){
    return x.w > y.w;
}
struct nn{
    int u, v, w;
}ee[N];
int dis[4][210], vis[210];
void add(int u, int v, int w){
    ee[++cnt].u = now[u];
    now[u] = cnt;
    ee[cnt].v = v;
    ee[cnt].w = w;
}
void dij(int s){
    priority_queue<Node> q;
    memset(vis, 0, sizeof(vis));
    for(int i = 1; i <= n; i++) dis[s][i] = 1e6;
    dis[s][s] = 0;
    q.push(Node{s, 0});
    while(!q.empty()){
        Node tmp = q.top();
        q.pop();
        int u = tmp.u;
        if(vis[u]) continue;
        vis[u] = 1;
        for(int i = now[u]; i; i = ee[i].u){
            int v = ee[i].v;
            if(dis[s][u] + ee[i].w < dis[s][v]){
                dis[s][v] = dis[s][u] + ee[i].w;
                q.push(Node{v, dis[s][v]});
            }
        }
    }

}
int main(){
    cin >> t;
    while(t--){
        memset(now, 0, sizeof(now));
        memset(ee, 0, sizeof(ee));
        cnt = 0;
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> e[i].x >> e[i].y >> e[i].r;
        for(int i = 1; i <= n; i++){
            add(i, i, 0);
            for(int j = i + 1; j <= n; j++){
                double tmp = sqrt((double)(e[i].x - e[j].x) * (e[i].x - e[j].x) + (e[i].y - e[j].y) * (e[i].y - e[j].y));
                if(tmp <= e[i].r + e[j].r) add(i, j, 1), add(j, i, 1);
                else add(i, j, 1e6), add(j, i, 1e6);
            }
        }
        dij(1);
        dij(2);
        dij(3);
        int ans = 1e6;
        int f = 0;
        if(dis[1][2] == 1e6 || dis[1][3] == 1e6 || dis[2][3] == 1e6) f = 1;
        if(f) cout << -1 << "\n";
        else{
            for(int i = 1; i <= n; i++) ans = min(ans, dis[1][i] + dis[2][i] + dis[3][i]);
            cout << n - ans - 1 << "\n";
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值