hdu4435 charge-station(先建后拆+bfs)
charge-station
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 617 Accepted Submission(s): 310
Problem Description
There are n cities in M^3's empire. M^3 owns a palace and a car and the palace resides in city 1. One day, she wants to travel around all the cities from her palace and finally back to her home. However, her car has limited energy and can only travel by no more than D meters. Before it was run out of energy, it should be charged in some oil station. Under M^3's despotic power, the judge is forced to build several oil stations in some of the cities. The judge must build an oil station in city 1 and building other oil stations is up to his choice as long as M^3 can successfully travel around all the cities.
Building an oil station in city i will cost 2 i-1 MMMB. Please help the judge calculate out the minimum cost to build the oil stations in order to fulfill M^3's will.
Building an oil station in city i will cost 2 i-1 MMMB. Please help the judge calculate out the minimum cost to build the oil stations in order to fulfill M^3's will.
Input
There are several test cases (no more than 50), each case begin with two integer N, D (the number of cities and the maximum distance the car can run after charged, 0 < N ≤ 128).
Then follows N lines and line i will contain two numbers x, y(0 ≤ x, y ≤ 1000), indicating the coordinate of city i.
The distance between city i and city j will be ceil(sqrt((xi - xj) 2 + (yi - yj) 2)). (ceil means rounding the number up, e.g. ceil(4.1) = 5)
Then follows N lines and line i will contain two numbers x, y(0 ≤ x, y ≤ 1000), indicating the coordinate of city i.
The distance between city i and city j will be ceil(sqrt((xi - xj) 2 + (yi - yj) 2)). (ceil means rounding the number up, e.g. ceil(4.1) = 5)
Output
For each case, output the minimum cost to build the oil stations in the binary form without leading zeros.
If it's impossible to visit all the cities even after all oil stations are build, output -1 instead.
If it's impossible to visit all the cities even after all oil stations are build, output -1 instead.
Sample Input
3 3
0 0
0 3
0 1
3 2
0 0
0 3
0 1
3 1
0 0
0 3
0 1
16 23
30 40
37 52
49 49
52 64
31 62
52 33
42 41
52 41
57 58
62 42
42 57
27 68
43 67
58 48
58 27
37 69
Sample Output
11
111
-1
10111011
Hint
In case 1, the judge should select (0, 0) and (0, 3) as the oil station which result in the visiting route: 1->3->2->3->1. And the cost is 2^(1-1) + 2^(2-1) = 3.
Source
讲解我就拷一下杜娘的,讲的挺好的,代码写的也好,还没想出再优化的方法
题意:
n个城市,一个人想从城市1出发,一次访问n个城市再回到城市1。
他的交通工具车在加过油后只能走d米。
可以在各个城市建造加油站,建造费用cost为2^(i-1)
问能使他访问所有城市且回到1点所需要建造加油站的最小花费,以2进制形式输出。
分析:
1、加油站的建造费用太特殊了,是2^(i-1)。之前1到i-1城市的加油站都建的所有花费也比只
建第i个城市的加油站花费小。自己算一下,等比数列求和结果是:(也可以结合二进制来看)
1到i-2个城市都建加油站的花费为 2^(i-1)-1 。(恰好比只建第i个城市的花费小1)
这说明为了使达到目标的花费最小,尽量不要在编号大的城市建加油站。
很容易想到贪心。先假设所有点都建,然后依次从编号大的删点,看看能不能遍历整个图。
dis[i]表示与点i距离最近的加油站的距离。
2、
输出的答案是2进制,由费用10进制转化过来就是在第i个点建立加
油站,答案从右往左数第i个值就为1。
3、解题步骤:
(1) 第一步判断所有点都建立加油站能不能完成题目的要求,不能输出-1。
能完成要求的话,我们注意到建站费用是和点的编号有关的,
比如第i个点建站的费用是等于前i-1个点都建站的费用+1,二进制的规律。
然后我们可以从后往前判断当前加油站能不能拆。
(2) dis数组存从当前点到最近的加油站的距离,判断分两个方面:
如果当前点也有加油站,dis[i] <= D就可以;
如果当前点决定不建立加油站,那么dis[i]要小于D/2;
不符合要求就不能拆这个加油站。
感想:这先建再拆的做法真是头一次见啊,不过确实很好
#include<stdio.h> #include<math.h> #include<string.h> #define inf 10000000; struct node { int x,y,id; }s[130]; int D,N,st[1000000],dis[130],e[130][130],vis[130],ok[130],dist[130]; int clc(int i,int j)//向上取整 { double ss; int x; ss=sqrt((double)(s[i].x-s[j].x)*(s[i].x-s[j].x)+(double)(s[i].y-s[j].y)*(s[i].y-s[j].y)); x=ss; if(ss-x<0.000001) return x; else return x+1; } int min(int x,int y) { return x<y?x:y; } int bfs() { memset(vis,0,sizeof(vis)); int t=0,w=1,i,j; for(i=1;i<=N;i++) if(ok[i]) dis[i]=0;//本身为加油站到最近加油站距离为0 else dis[i]=inf; st[0]=1; vis[1]=1; while(t<w) { j=st[t]; for(i=1;i<=N;i++) { if(!vis[i]&&e[j][i]<=D)//没访问过并且到最近加油站距离小于等于D { dis[i]=min(dis[i],dis[j]+e[j][i]);//更新到周围加油站的最短距离, if(ok[i]) { st[w++]=i; vis[i]=1;//这里只标记了加油站 } } } t++; } for(i=1;i<=N;i++) { if(ok[i]&&!vis[i])//有加油站但是无法到达 return 0; if(!ok[i]&&dis[i]*2>D)//没加油站但是到周围最短距离大于(D+1)/2 return 0; } return 1; } int solve() { int i,j=N,k; for(i=1;i<=N;i++)//最开始假设所有的点都建加油站 ok[i]=1; if(!bfs()) { puts("-1"); return 0; } for(i=N;i>0;i--)//从高到低能拆就拆 { ok[i]=0; if(!bfs()) ok[i]=1; } while(!ok[j])//除去前面的0 j--; for(;j>0;j--)//这方法输出二进制还真是巧妙得很啊 printf("%d",ok[j]); puts(""); } int judge()//这个是我添加的一个判断是否有点到周围距离都大于D的,要不要对运行时间没影响 { int i,j; for(i=1;i<=N;i++) dist[i]=inf; for(i=1;i<=N;i++) { for(j=i+1;j<=N;j++) { if(dist[i]>e[i][j]) dist[i]=e[i][j]; if(dist[j]>e[i][j]) dist[j]=e[i][j]; } if(dist[i]>D) { puts("-1"); return 0; } } return 1; } int main() { int i,j,k,l; while(scanf("%d%d",&N,&D)!=EOF) { for(i=1;i<=N;i++) { scanf("%d%d",&s[i].x,&s[i].y); } for(i=1;i<N;i++) for(j=i;j<=N;j++) e[j][i]=e[i][j]=clc(i,j); if(!judge()) continue; solve(); } return 0; }