HDU 4435 charge-station (搜索+YY)

题意:有一个人要从1点出发把所有点遍历一遍并且最后能回到1点,其中每隔d距离就要加一次油,现在我们在各个点设一些加油站(1点必须设),且每隔点建加油站的费用最小为2^(i-1),求满足条件下的最小花费。

分析:思考了半天也不知道按照什么顺序来搜索,现给出大神思路:

突破口在于在i号点建立加油站的费用为2^(i-1),这样特殊的花费会使得我们有一个贪心的规律,就是尽量不在号比较大的点建加油站,如果在n号点建立加油站的费用会大于在除n以外的所有点都建加油站的总费用。所以我们可以先尝试把除n以外的所有点建立加油站,观察是否满足要求。若满足则说明我们必然不会在n点建立加油站,若不满足我们就一定要在n点建加油站。然后就不用再考虑n点了,在确定了n点之后,我们用同样的方法来观察n-1号点是否需要建立加油站,即将1~n-2号点都建立加油站,观察是否满足要求。以此类推,可以推出所有点的情况。

接下来我们需要解决是否满足要求的问题了。分为两部判断,1.判断所有加油站是否可达(从1号点开始广搜,若到当前点距离<=d则入队)。2.判断其余点是否可达(刚才的广搜过程可以顺便标出每个点到最近的加油站的距离,要求能从加油站到该点并返回加油站,所以点到加油站的距离必须小于等于d/2)。若满足这两点必然符合要求,否则不符合要求。满足要求的话,该点无需建立加油站了。


Orz..................................................................................Orz


#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <set>
#include <queue>
#include <stack>
#include <climits>//形如INT_MAX一类的
#define MAX 100005
#define INF 0x7FFFFFFF
#define REP(i,s,t) for(int i=(s);i<=(t);++i)
#define ll long long
#define mem(a,b) memset(a,b,sizeof(a))
#define mp(a,b) make_pair(a,b)
#define L(x) x<<1
#define R(x) x<<1|1
# define eps 1e-5
//#pragma comment(linker, "/STACK:36777216") ///传说中的外挂
using namespace std;

int dis[130][130];
int vis[130],x[130],y[130],build[130];
int n,D,flag;
int q[222];
int head,tail;
int getdis(int x1,int y1,int x2,int y2) {
    double a1 = x1 * 1.0; double b1 = y1 * 1.0;
    double a2 = x2 * 1.0; double b2 = y2 * 1.0;
    double s = sqrt((a1-a2)*(a1-a2) + (b1-b2)*(b1-b2));
    return ceil(s);
}

void init() {
    memset(build,0,sizeof(build));
}

int bfs(int v0) { // 判断除去v0点,其他点能否符合要求
    head = 0;
    tail = 0;
    memset(vis,0,sizeof(vis));
    int cnt = 1; //点v0
    q[head++] = 1;
    vis[1] = 1;
    while(head != tail) {
        int t = q[tail++];
        for(int i=1; i<=n; i++) {
            if(vis[i] == 0) {
                if(dis[t][i] <= D && (build[i] || i < v0)) {
                    q[head++] = i;
                    vis[i] = 1;
                    cnt ++;
                } else {
                    if(dis[t][i] <= D/2) {
                        vis[i] = 1;
                        cnt ++;
                    }
                }
                if(cnt == n) return 1;
            }
        }
    }
    return 0;
}

int main(){
    while(scanf("%d%d",&n,&D) != EOF) {
        init();
        for(int i=1; i<=n; i++) {
            scanf("%d%d",&x[i],&y[i]);
        }
        for(int i=1; i<=n; i++) {
            for(int j=1; j<=n; j++) {
                dis[i][j] = getdis(x[i],y[i],x[j],y[j]);
            }
        }
        if(bfs(n+1) == 0) { //自己加一个点,则如果已有的n的点不满足要求
            printf("-1\n");
            continue;
        }
        for(int i=n; i>1; i--) {
            if(bfs(i) == 0) { //i-1个点不满足要求,i点必然要建
                build[i] = 1;
            }
        }
        int ok = 0;
        for(int i=n; i>1; i--) {
            if(build[i] == 1 || ok) {
                printf("%d",build[i]);
                ok = 1;
            }
        }
        printf("1\n");
    }
    return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值