HDU 4435 charge-station
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4435
题目大意是给出N个二维坐标点代表N个城市,让你选择几个城市建加油站,使得能从1号城市出发遍历所有城市再回来,并且每加一次油的行驶距离为D,不能让车抛锚了。建立一个加油站需要一定的花费,现在要你求花费最少的建站方案。
题目有个突破口是在第i个城市建加油站的花费是2(i-1),花费刚好就是个N位的二进制,如果在第i位建站,费用比前i-1个城市都建站还高,所以这里可以用贪心,编号越高的城市能不建站就不建站,这样就能保证花费最小。确定能不能建站的方法是如果该城市不建站,从1出发存在一个路径使得能遍历所有城市并返回,则该城市不要建站。所以每确定一个城市要不要建站就需要找一下合法路径。
由于不需要确切的路径,于是只要判断能否经过所有的点,用类似染色的搜索就可以了,用BFS或DFS都可以。关键是染色的转移条件,如果起点和目标点都没有建站就不能转移,如果两点都建站则要求两点距离<=D,一点建站一点不建站的话要<=D/2;初一看这个规则会把一些情况给漏了,有可能借助一个不建站的B点从一个建站A点到另一个建站C点,这两一进一出有可能有一边的长度大于D/2,且总和不超过D,这样也是满足条件的,但我规则中没涉及这种情况,原因是有替代的方案,虽然不是总路程最优的:既然总和不超D,何不直接从A到C,有一边的距离超过D/2,那必有一边不超过D/2(假设是AB边),按规则来做就是从A到B再回到A加油,这样就和没有B点一样了。如果A、B都是不建站点那没必要现在就判断,应为如果能从A到B再返回一个建站点,那么B一定能直接从建站点过来再回去,在之后的搜索会涉及到。
我是用DFS来搜索染色的,代码如下。
1 #include <stdio.h> 2 #include <string.h> 3 #include <math.h> 4 struct node 5 { 6 int x,y; 7 void read(){scanf("%d%d",&x,&y);} 8 }data[128]; 9 int n,d,cost[128][128]; 10 bool ifv[128],ans[128]; 11 inline int dist(const node &a,const node &b) 12 { 13 return (int)ceil(sqrt((double)((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)))); 14 } 15 void init() 16 { 17 for (int i=0;i<n;++i) 18 for (int j=0;j<n;++j) 19 { 20 cost[i][j]=cost[j][i]=dist(data[i],data[j]); 21 } 22 } 23 int search(int k) 24 { 25 int tem=1; 26 ifv[k]=true; 27 for (int i=0;i<n;++i) 28 { 29 if (i==k || ifv[i] || (!ans[k] && !ans[i]))continue; 30 if (ans[i] && cost[i][k]<=d && ans[k]) {tem+=search(i);continue;} 31 if (cost[i][k]<=d>>1) tem+=search(i); 32 } 33 return tem; 34 } 35 inline bool check() 36 { 37 memset(ifv,0,sizeof ifv); 38 if (search(0)!=n) return false; 39 return true; 40 } 41 int main() 42 { 43 while (~scanf("%d%d",&n,&d)) 44 { 45 memset(ans,true,sizeof ans); 46 for (int i=0;i<n;++i) data[i].read(); 47 init(); 48 if (!check()) {puts("-1");continue;} 49 for (int i=n-1;i>0;--i) 50 { 51 ans[i]=false; 52 if (!check()) ans[i]=true; 53 } 54 int j=n-1; 55 while (!ans[j])--j; 56 while (j>=0) {if (ans[j]) printf("1"); else printf("0");--j;} 57 printf("\n"); 58 } 59 }