2012天津赛区现场赛的E题,刚才做模拟比赛的时候和安神讨论了很久,思路没理清。
大致题意是 给出N个点,让你选择性地建立加油站,在第i个点建立加油站的费用为2^i-1,要使自己能从1号点经过所有点回到原点,
点可以重复经过,加油费用不计,每次加油最多能跑的距离为D。输出的答案是2进制,由费用10进制转化过来就是在第i个点建立加
油站,答案从右往左数第i个值就为1。
第一步判断所有点都建立加油站能不能完成题目的要求,不能输出-1。
能完成要求的话,我们注意到建站费用是和点的编号有关的,比如第i个点建站的费用是等于前i-1个点都建站的费用+1,二进制的规律。
然后我们可以从后往前判断当前加油站能不能拆。
dist数组存从当前点到最近的加油站的距离,判断分两个方面:
如果当前点也有加油站,dist[i] <= D就可以;
如果当前点决定不建立加油站,那么dist[i]要小于D/2;
不符合要求就不能拆这个加油站。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <queue> #include <algorithm> using std::queue; using std::min; const int MAXN = 130; const int INF = 0x3f3f3f3f; struct point { int x, y; }p[MAXN]; int n, d; int g[MAXN][MAXN], dist[MAXN], sta[MAXN]; bool vis[MAXN]; int pf(int i) { return i * i; } double distance(int i, int j) { return sqrt((double)(pf(p[i].x - p[j].x) + pf(p[i].y - p[j].y))); } void ReadGraph() { //让点号从0开始则每个点建立加油站的费用2^i for(int i = 0; i < n; i ++) { scanf("%d%d", &p[i].x, &p[i].y); } for(int i = 0; i < n; i ++) for(int j = 0; j < n; j ++) { g[i][j] = (int)ceil(distance(i, j)); } } bool judge() { queue<int> q; memset(vis, false, sizeof vis); for(int i = 0; i < n; i ++) { if(!sta[i]) dist[i] = INF; else dist[i] = 0; } vis[0] = true; dist[0] = 0; q.push(0); while(!q.empty()) { int u = q.front(); q.pop(); for(int i = 0; i < n; i ++) { if(!vis[i] && g[u][i] <= d) { dist[i] = min(dist[i], dist[u] + g[u][i]); if(sta[i]) { vis[i] = true; q.push(i); } } } } for(int i = 0; i < n; i ++) { if(sta[i] && !vis[i]) return false; else if(!sta[i] && dist[i] * 2 > d) return false; } return true; } void solve() { for(int i = 0; i < n; i ++) { sta[i] = 1; } if(!judge()) { puts("-1"); return; } for(int i = n - 1; i >= 1; i --) { sta[i] = 0; if(judge()) continue; sta[i] = 1; } int cur = n - 1; while(!sta[cur]) cur --; for( ; cur >= 0; cur --) { printf("%d", sta[cur]); } printf("\n"); } int main() { //freopen("test.in", "r", stdin); while(scanf("%d%d", &n, &d) != EOF) { ReadGraph(); solve(); } return 0; }