6.5 竞赛题目选讲

Undraw_the_Trees

点击查看笔者代码
#include<iostream>
#include<vector>
using namespace std;

const int maxn = 20000+5, maxl = 200+5;
int cnt = 0, sum = 0;
string tree[maxl];

struct Node{
  string name;
  vector<Node*> v;	 
} node[maxn];

Node* build(int p, int s, int e, string name) {
  Node* root = &node[cnt++];
  root->v.clear();
  root->name = name;
  if(p >= sum) return root;
  for(int i = s; i < e; i++) {
  	if(tree[p][i] != ' ' && tree[p][i] != '#' && tree[p][i] != '-' && tree[p][i] != '|') {
	  name = tree[p][i];
	  if(p+1 < sum && tree[p+1][i] == '|') {
	  	int ts, te;
	  	for(ts=i, te=i; ; ) { 
	  	  if(ts > -1 && tree[p+2][ts-1]=='-') ts--;
	  	  else if(te < tree[p+2].length() && tree[p+2][te+1]=='-') te++;
	  	  else break;
		}
		if(te > tree[p+3].length()-1) te = tree[p+3].length()-1;
		root->v.push_back(build(p+3, ts, te+1, name));
	  }else{
	  	root->v.push_back(build(p+3, -1, -1, name)); 
	  }	
	}
  }
  return root;
}

void print_tree(Node* root) {
  cout << root->name;
  cout << "(";
  for(int i = 0; i < root->v.size(); i++) print_tree(root->v[i]);
  cout << ")";
}

int main() {
//	freopen("test.in", "r", stdin);
//	freopen("test.out", "w", stdout);
  int t;
  cin >> t;
  while(t--) {
    cnt = sum = 0;
    string line;
	while(getline(cin, line) && line != "#") {
	  tree[sum++] = line;
	}  
	string name = " ";
    int pos = 0;
    for(; pos < sum; pos++) {
      for(int i = 0; i < tree[pos].length(); i++) 
	    if(tree[pos][i] != ' ') {
	      name = tree[pos][i];
		  break;
	    }
      if(name != " ") break;	
	}
	Node* root = build(pos+3, 0, tree[pos+3].length(), name);
	cout << "(";
	if(name != " ") print_tree(root);
	cout << ")";
    cout << endl;
  }
  return 0;
}

又是一道模拟题,笔者原先了解错了要求,因此一直WA,这边需要尤为注意的一个地方就是,'-'的覆盖范围可能超过下一个字符串的长度(如果按照笔者这样存储的话),此时需要进行特判修改
同时因为这是一道递归建树题目,并且对树没有修改,所以可以在递归建树的过程中直接进行输出,也就是根本不需要建树,只能说作者的思路更加清晰
做题目前最好给自己一些思考时间,更加看透问题的本质,否则后面会花费大量时间进行调试,得不偿失,越是看似简单的问题越要小心
作者分析道:直接在二维字符数组里面递归即可,无须建树,注意对空树的处理,以及结点标号可以是任意可打印字符。代码如下:

点击查看代码
#include<cstdio>
#include<cctype>
#include<cstring>
using namespace std;

const int maxn = 200 + 10;
int n;
char buf[maxn][maxn];

//递归遍历并且输出以字符buf[r][c]为根的树 
void dfs(int r, int c) {
  printf("%c(", buf[r][c]);
  if(r+1 < n && buf[r+1][c] == '|') { //有子树
    int i = c; 
  	while(i-1 >= 0 && buf[r+2][i-1] == '-') i--;//找“----”的左边界
	while(buf[r+2][i] == '-' && buf[r+3][i] != '\0') {
	  if(!isspace(buf[r+3][i])) dfs(r+3, i);//fgets读入的'\n'也满足isspace()//空白符指空格、水平制表、垂直制表、换页、回车和换行符,isspace函数的作用就是判断是否是空白符 
	  i++; 
	} 
  }
  printf(")");
}
/*
注意fgets的使用,fgets(char buf[], int n, stdin)从stdin中读取最多(n-1)个字符,注意fgets并不是读取一行才会停止,n的优先级在某些情况下高于换行
fgets读取到\n的时候会结束,如果此时还没有到n-1个字符,那么他会存储,也就是说fgets只是以\n作为终止条件之一,并不是其不会读取换行符,
这也是需要注意的地方 
*/ 
void solve() {
  n = 0;
  for(;;) {
  	fgets(buf[n], maxn, stdin);
  	if(buf[n][0] == '#') break; else n++;
  }
  printf("(");
  if(n) {
  	for(int i = 0; i < strlen(buf[0]); i++)
  	  if(buf[0][i] != ' ') { dfs(0, i); break; }
  }
  printf(")\n");
}

int main() {
  int T;
  fgets(buf[0], maxn, stdin);
  sscanf(buf[0], "%d", &T);
  while(T--) solve();
  return 0;
}

剩下的题目暂时搁置,笔者决定这个寒假先总览全局,先跑路了┭┮﹏┭┮

笔者又回来了,接下来讲述的是Sculpture的题解
这题主要是离散化的考察,离散化是对于大量松散数据的集中,通常的顺序是
1.将其中需要的数据放入数组中
2.将该数组进行排序,去重
3.根据排序的顺序对各个数据进行编号
该题如果使用三维1000的数组去模拟效率是十分低下的,同时会浪费大量资源,此时离散化就能将该数据大大简化成三维100的数组
这边作者仍然采用空气快的方法(注意在创建的时候在最外面的存储加上一各预留空间作为空气,使得空气能到达任何一处,类似超级原点,超级终点的思想)
这边巧妙地将面积转化成与空气接触的面积,但是其实离散化后,直接统计也是可以的,但是因为里面的空间都属于需要被处理的物体,因此此时空气快的方法是更好的
代码如下:

点击查看笔者代码
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;

const int maxn = 50 + 5, maxl = 1000 + 10;
int n, m, mx, my, mz, dx[maxn*2], dy[maxn*2], dz[maxn*2], v[maxn*2][maxn*2][maxn*2], dir[6][3] = {1,0,0, 0,1,0, 0,0,1, -1,0,0, 0,-1,0, 0,0,-1};

struct Node{
  int x0, y0, z0, x1, y1, z1, a0, b0, c0, a1, b1, c1;
  Node(int x0 = 0, int y0 = 0, int z0 = 0, int x1 = 0, int y1 = 0, int z1 = 0) : x0(x0), y0(y0), z0(z0), x1(x1), y1(y1), z1(z1) {};	
} node[maxn];

void getD() {
  cin >> m;
  for(int i = 0; i < m; i++) {
  	cin >> node[i].x0 >> node[i].y0 >> node[i].z0 >> node[i].x1 >> node[i].y1 >> node[i].z1;
  	node[i].x1 += node[i].x0; node[i].y1 += node[i].y0; node[i].z1 += node[i].z0;
  } 
  memset(dx, 0, sizeof(dx));
  memset(dy, 0, sizeof(dy));
  memset(dz, 0, sizeof(dz));
}

void getOrder() {
  int pos[maxl], cnt = 0; memset(pos, 0, sizeof(pos));
  for(int i = 0; i < m; i++) pos[node[i].x0] = pos[node[i].x1] = 1;
  for(int i = 0; i < maxl; i++) if(pos[i]) { cnt++; pos[i] = cnt; dx[cnt] = i; }
  if(!pos[1000]) { cnt++; pos[1000] = cnt; dx[cnt] = 1000; } //不含x 
  dx[cnt+1] = 1000; mx = cnt;
  for(int i = 0; i < m; i++) { node[i].a0 = pos[node[i].x0]; node[i].a1 = pos[node[i].x1]; }
  memset(pos, 0, sizeof(pos)); cnt = 0;
  for(int i = 0; i < m; i++) pos[node[i].y0] = pos[node[i].y1] = 1;
  for(int i = 0; i < maxl; i++) if(pos[i]) { cnt++; pos[i] = cnt; dy[cnt] = i; }
  if(!pos[1000]) { cnt++; pos[1000] = cnt; dy[cnt] = 1000; } //不含y 
  dy[cnt+1] = 1000; my = cnt;
  for(int i = 0; i < m; i++) { node[i].b0 = pos[node[i].y0]; node[i].b1 = pos[node[i].y1]; }
  memset(pos, 0, sizeof(pos)); cnt = 0;
  for(int i = 0; i < m; i++) pos[node[i].z0] = pos[node[i].z1] = 1;
  for(int i = 0; i < maxl; i++) if(pos[i]) { cnt++; pos[i] = cnt; dz[cnt] = i; }
  if(!pos[1000]) { cnt++; pos[1000] = cnt; dz[cnt] = dz[cnt+1] = 1000; mz = cnt; } //不含z 
  dz[cnt+1] = 1000; mz = cnt;
  for(int i = 0; i < m; i++) { node[i].c0 = pos[node[i].z0]; node[i].c1 = pos[node[i].z1]; }
}

void paint() {
  for(int i = 0; i < m; i++) 
    for(int j = node[i].a0; j < node[i].a1; j++)
      for(int k = node[i].b0; k < node[i].b1; k++)
        for(int l = node[i].c0; l < node[i].c1; l++)
          v[j][k][l] = 1;
}

void bfs(int& area, int& vol) {
  queue<Node> q; q.push(Node(0, 0, 0)); v[0][0][0] = -1;
  while(!q.empty()) {
   	Node temp = q.front(); q.pop();	
  	vol += (dx[temp.x0+1]-dx[temp.x0]) * (dy[temp.y0+1]-dy[temp.y0]) * (dz[temp.z0+1]-dz[temp.z0]);
	int tx, ty, tz;
	for(int i = 0; i < 6; i++) {
      tx = temp.x0+dir[i][0]; ty = temp.y0+dir[i][1]; tz = temp.z0+dir[i][2];  
      if(tx >= 0 && tx <= mx && ty >= 0 && ty <= my && tz >= 0 && tz <= mz) {
      	if(v[tx][ty][tz] == 1) {
		  if(tx != temp.x0) area += (dy[ty+1]-dy[ty]) * (dz[tz+1]-dz[tz]);
		  else if(ty != temp.y0) area += (dx[tx+1]-dx[tx]) * (dz[tz+1]-dz[tz]);
		  else area += (dx[tx+1]-dx[tx]) * (dy[ty+1]-dy[ty]);
		}
		else if(!v[tx][ty][tz]) { v[tx][ty][tz] = -1; q.push(Node(tx, ty, tz)); }
	  } 
	}
  }  
  vol = 1000*1000*1000 - vol;
}

int main() {
  cin >> n;
  while(n--) {
    int area = 0, vol = 0;
  	getD();
    getOrder();
    memset(v, 0, sizeof(v));
    paint();
    bfs(area, vol);
    cout << area << " " << vol << endl;
  }
  return 0;
} 

self-Assembly题解
本题很明显是一道判断图是否有环,笔者通过dfs同时需要注意正方形此时并不是判断是成环的关键,而是如果一条边被dfs到第二次的时候才是成环的关键

点击查看笔者代码
#include<iostream>
#include<vector>
#include<string>
#include<cstring>
using namespace std;

const int maxn = 261 + 10, maxl = 40000 + 10;
int t;
bool dis[maxl][4], unable[maxl][4];
vector<int> n[maxn];

struct Mat{
  int dir[4];
} mat[maxl];

int chartoint(string s) {
  if(s[0]=='0') return 0;
  return (s[0]-'A'+1)*10 + (s[1]=='+' ? 1 : 0);
}

void getD() {
  string line; 
  for(int i = 0; i < maxn; i++) n[i].clear();
  for(int i = 0; i < t; i++) {
  	cin >> line;
  	for(int j = 0; j < 4; j++) {
  	  int temp = chartoint(line.substr(j*2));	
	  mat[i].dir[j] = temp;
	  if(n[temp].empty() || n[temp][n[temp].size()-1] != i) n[temp].push_back(i); 
	} 
  }
}

bool dfs(int s, int from) {
  if(from+1) dis[s][from] = true;
  for(int i = 0; i < 4; i++) 
    if(i!=from && !unable[s][i] && mat[s].dir[i]) {
      int temp = mat[s].dir[i] ^ 1; 
      if(!n[temp].empty()) {  
      	int len = n[temp].size(); 
      	for(int j = 0; j < len; j++) {
      	  for(int k = 0; k < 4; k++) {
      	    if(mat[n[temp][j]].dir[k]==temp && !unable[n[temp][j]][k]) {
      	      if(dis[n[temp][j]][k]) return true;
			  if(dfs(n[temp][j], k)) return true;
			  else {
			  	for(int l = 0; l < 4; l++)
				  if(l != k) unable[n[temp][j]][l] = true;	
			  } 
			}
		  }	
		}
	  }
	}
  if(from+1) dis[s][from] = false;
  return false;
}

int main() {
  while(scanf("%d", &t) == 1) {
  	bool flag = true; 
  	getD();
    memset(dis, 0, sizeof(dis));
    memset(unable, 0, sizeof(unable));
    for(int i = 0; i < t; i++) {
      if(unable[i][0] && unable[i][1] && unable[i][2] && unable[i][3]) continue;
	  if(dfs(i, -1)) { cout << "unbounded" << endl; flag = false; break; }	
	}
	if(flag) cout << "bounded" << endl;
  }
  return 0;
} 

作者在这边推荐了将正方形更加抽象化,把标号看成点,正方形看成边,那么最后就会成为判断是否为DAG就可以了,也就是做一次拓扑排序即可(这边笔者还需要理解一下,不知道为什么要用拓扑排序)

点击查看笔者尝试代码
#include<iostream>
#include<cstring>
#include<string>
#include<vector>
using namespace std;

const int maxn = 52+5;
int t;
bool sign[maxn][maxn], dis[maxn][maxn], graph[maxn][maxn];

int chartoint(string s) {
  if(s[0] == '0') return 0;
  else return s[0]-'A'+1+(s[1]=='+' ? 26 : 0);
}

void getD() {
  string line;
  memset(dis, 0, sizeof(dis));
  memset(graph, 0, sizeof(graph));
  memset(sign, 0, sizeof(sign));
  for(int i = 0; i < t; i++) {
  	cin >> line;
  	vector<int> v;
  	for(int j = 0; j < 4; j++) {
  	  int temp = chartoint(line.substr(j*2));
	  if(temp) v.push_back(temp);	
	}
  	for(int j = 0; j < v.size(); j++)
  	  for(int k = j+1; k < v.size(); k++)
  	    graph[v[j]][v[k]] = graph[v[k]][v[j]] = true;
  }
  for(int i = 1; i <= 52; i++) graph[0][i] = true;
}

bool dfs(int s) {
  for(int i = 1; i <= 52; i++) {
  	if(graph[s][i] && !sign[s][i]) {
  	  sign[s][i] = true;
  	  int temp = i > 26 ? i-26 : i+26;
	  if(dis[i][temp]) return true;
	  dis[i][temp] = true;
	  if(dfs(temp)) return true;
	  dis[i][temp] = false;
	  sign[s][i] = false;	
	}
  }
  return false;
}

int main() {
  while(scanf("%d", &t) == 1) {
  	getD();
  	if(dfs(0)) cout << "unbounded" << endl;
  	else cout << "bounded" << endl;
  }
  return 0;
}

self-assembly因为正方形的数量是无限的,并且所需要做的仅仅只是判断是否构成无限组合,那么我们这时候可以将正方形抽象化,不失一般性的,我们假设正方形上此时有A+00B+C+,那么我们就可以将其抽象为A+->B-,A+->C-,B+->A-,B+->C-,C+->A-,C+->B-这六条路径,也就是此时正方形拓扑变换成k条边,那么原题简化成有向图是否有环,考虑到我们拓扑排序的时候恰好需要判断是否为DAG,因此我们可以借助拓扑变换来实现

Ideal_Path:

本题笔者使用的是优先队列,将父亲路线的字典序大小作为第一判断条件,当前将前往的结点color作为第二判断条件,很明显,在该判断条件下,一个结点被经历有且仅有一次,随后的经历都不是最优的,符合BFS的条件,直接化成BFS模板题

点击查看笔者代码
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<algorithm>
#include<stack>
using namespace std;

typedef unsigned long long ull;
constexpr int maxn = 100000 + 10;
constexpr ull base = 10e9;
bool vis[maxn];
int fa[maxn][2];

struct Road{
  int to, color;
  Road(int to = 0, int color = 0) : to(to), color(color) {}
  bool operator < (const Road &r) const {
    return color < r.color;
  }
};
vector<Road> node[maxn];

struct Node{
  int from, to, cost, color;
  ull order;
  Node(int from = 0, int to = 0, int cost = 0, int color = 0, ull order = 0) : from(from), to(to), cost(cost), color(color), order(order) {}
  bool operator < (const Node& n) const {
    if(cost != n.cost) return cost > n.cost;
    if(order != n.order)return order > n.order;
    return to > n.to;
  }
};

bool getD(int& n, int& m) {  
  memset(vis, 0, sizeof(vis));
  memset(fa, 0, sizeof(fa));
  if(scanf("%d%d", &n, &m) != 2) return false;
  int from, to, color;
  for(int i = 0; i < m; i++) {
  	scanf("%d%d%d", &from, &to, &color);
  	node[from].push_back(Road(to, color));
  	node[to].push_back(Road(from, color));
  }
  for(int i = 1; i <= n; i++) sort(node[i].begin(), node[i].end());
  return true;
}

void output(int &ans, int &n) {
  printf("%d\n", ans);
  stack<int> s;
  int pos = n; 
  while(pos!=1) {
  	s.push(fa[pos][1]);
    pos = fa[pos][0];
  }
  while(true) {
    printf("%d", s.top());
	s.pop();
	if(!s.empty()) printf(" ");
    else break;
  } 
  printf("\n");
}

int main() {
  int n, m, ans;	
  while(getD(n, m)) {
  	priority_queue<Node> q;
    q.push(Node(0, 1, 0, 0, 0)); 
    int cur = 0;
    ull preorder = 0, cnt = 0;
    while(!q.empty()) { 
  	  Node temp = q.top(); q.pop(); 
	  if(vis[temp.to]) continue; 
  	  vis[temp.to] = true;
  	  if(temp.cost == cur) {
	    if(preorder != temp.order) {
	  	  cnt++;
	  	  preorder = temp.order;
	    }	
	  } 
	  else{ 
	    cur++; 
	    preorder = temp.order; 
	    cnt = 0; 
	  }
  	  fa[temp.to][0] = temp.from; 
  	  fa[temp.to][1] = temp.color;
	  if(temp.to == n) { ans = temp.cost; break; }
  	  int len = node[temp.to].size();
	  for(int i = 0; i < len; i++) {
	    int nt = node[temp.to][i].to;
	    if(!vis[nt]) {
	  	  q.push(Node(temp.to, nt, temp.cost+1, node[temp.to][i].color, cnt*base+node[temp.to][i].color));
	    }
	  }
    }
    output(ans, n);
    for(int i = 1; i <= n; i++) node[i].clear();
  }
  return 0;
}

这边作者认为此时无需记录父节点也可以得到最短路,(本题应该是单汇最短路径的一种样式),方法是从终点开始倒着BFS,得到每个结点i到终点的最短距离d[i],然后直接从起点开始走,但是每次到达一个新结点时要保证d值恰好减少1(如果有多个选择则可以随便走),直到到达终点,可以证明,这样走过的路径一定是最短路(通过该种思想我们首先保证了最短路,或者可以认为这就是迭代加深搜索的弱化版本)
那么本题就可以转化为:直接从起点开始按照上述规则走,如果有多种走法,选颜色字典序最小的走,如果有多条边的颜色字典序都是最小,则记录所有这些边的终点,走下一步时要考虑从所有这些点出发的边,实际上是又做了一次BFS
本题也可以只进行一次BFS,直接从终点开始逆向进行,这边笔者认为应该需要借助优先队列来实现,以父节点的color为主要区分,重载<的时候注意,等以后笔者有空的时候来填坑-_-

System_Dependence:

较为简单的树的建造,不过需要区分显式和隐式安装的区别

点击查看代码
#include<iostream>
#include<string>
#include<map>
#include<vector>
#include<sstream>
#include<cstring>
using namespace std;

constexpr int maxn = 10000 + 10;
vector<string> v;
vector<int> out; 
map<string, int> m;
bool vis[maxn], depend[maxn];

void output() {
  for(int i = 0; i < out.size(); i++) cout << "   " << v[out[i]] << endl;
}

int main() {
  string line;
  memset(vis, 0, sizeof(vis));
  memset(depend, 0, sizeof(depend));
  while(getline(cin, line)) {
  	cout << line << endl;
  	if(line == "END") { break; } 
  	else if(line == "LIST") { 
	  output(); 
	}
  	else if(line.substr(0, 6) == "DEPEND") {
	  stringstream ss(line.substr(6));
	  string buf;
	  ss >> buf;
	  if(!m.count(buf)) { v.push_back(buf); m[buf] = v.size(); }
	  depend[m[buf]] = true; 
	  while(ss >> buf) {
	  	
	  }
	}
    else if(line.substr(0, 6) == "REMOVE") {
      cout << "remove" << endl;
	}
    else if(line.substr(0, 7) == "INSTALL") {
      cout << "install" << endl;
	}	
  }
  return 0;
} 

作者认为首先维护一个组件名字列表,这样可以把输入中的组件名全部转化为整数编号。接下来用两个vector数组depend[x]和depend2[x]分别表示组件x所依赖的组件列表和依赖于x的组件列表(即当读到DEPEND x y 时要把y加入depend[x],把x加入depend2[y]),这样就可以方便的安装,删除组件,以及判断某个组件是否需要了
为了区分显式安装和隐式,需要一个数组status[x],0表示组件x未安装,1表示隐式显式安装,2表示隐式安装,安装组建的代码如下:

点击查看代码
//depend[x]表示x所依赖的组件列表
//depend2[x]表示依赖于x的组件列表
//status[x]表示x的安装情况 

void install(int item, bool toplevel) {
  if(!status[item]) {//如果还没有安装 
  	for(int i = 0; i < depned[item].size(); i++)
  	  install(depend[item][i], false);//先把依赖的组件安装 
  	cout << "   Installing " << name[item] << "\n";
  	status[item] = toplevel ? 1 : 2;//看看是否是隐式显式安装 
  	installed.push_back(item);
  }
}

//删除的顺序相反:首先判断本组件是否能删除,如果可以删除,在删除之后再递归删除它所依赖的组件 
bool needed(int item) {
  for(int i = 0; i < depend2[item].size(); i++)
    if(status[depend2[item][i]]) return true;//看看依赖item的组件是否存在,如果存在那么item还被需要 
  return false;
}

void remove(int item, bool toplevel) {
  if((toplevel || status[item] == 2) && !needed(item)) {//要么是第一个要么是隐式安装并且不被需要可以删除 
  	status[item] = 0;//及时删除 
  	installed.erase(remove(installed.begin(), installed.end(), item), installed.end());//删除item 
  	cout << "   Removing " << name[item] << "\n";
  	for(int i = 0; i < depend[item].size(); i++)
  	  remove(depend[item][i], false);//删除依赖于item的组件列表 
  }
}

Painball:

本题笔者认为是离散化的实现,以及floodfill,具体来说就是找到再一起的圆圈,记录下有在边界的园,那么那一段区间一定是不可实现的,最后找出左右最大出入即可

点击查看代码
#include<iostream>
#include<cstring>
#include<iomanip>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;

constexpr int MAXN = 1000 + 10;
constexpr double EPS = 10e-10;
vector<double> sta, des;
bool vis[MAXN];

struct Node{
  double x, y, r;
  Node(double x = 0, double y = 0, double r = 0) : x(x), y(y), r(r) {}
} node[MAXN];

vector<Node> L, R;

bool getD(int& n) {
  if(scanf("%d", &n) != 1) return false;
  memset(vis, 0, sizeof(vis));
  L.clear(); R.clear();
  for(int i = 0; i < n; i++) cin >> node[i].x >> node[i].y >> node[i].r; 
  return true;
}

void dfs(int& n, double x, double y, double r, int pos) {
  vis[pos] = true;
  if(y - r <= 0) { sta.push_back(0); des.push_back(0); }
  if(y + r >= 1000) { sta.push_back(1000); des.push_back(1000); }
  if(x - r <= 0) { sta.push_back(y+sqrt(r*r-x*x)); sta.push_back(y-sqrt(r*r-x*x)); }
  if(x + r >= 1000) { des.push_back(y+sqrt(r*r-(x-1000)*(x-1000))); des.push_back(y-sqrt(r*r-(x-1000)*(x-1000))); }
  for(int i = 0; i < n; i++) {
  	if(vis[i]) continue;
  	if((node[i].x-x)*(node[i].x-x)+(node[i].y-y)*(node[i].y-y) <= (node[i].r+r)*(node[i].r+r)) dfs(n, node[i].x, node[i].y, node[i].r, i);
  }
}

bool insert(vector<double> &v, vector<Node>& goal) {
  if(v.size() < 2) return false; 
  sort(v.begin(), v.end());
  if(v[0] <= 0 && v[v.size()-1] >= 1000) { cout << "IMPOSSIBLE" << endl; return true; }
  goal.push_back(Node(v[0], v[v.size()-1])); 
  return false;
}

double Hash(vector<Node>& v) {
  bool sign[MAXN*2];
  memset(sign, 0, sizeof(sign));
  vector<double> temp;
  int len = v.size();
  for(int i = 0; i < len; i++) {
    temp.push_back(v[i].x); temp.push_back(v[i].y);
  }
  temp.push_back(0); temp.push_back(1000);
  sort(temp.begin(), temp.end());
  temp.erase(unique(temp.begin(), temp.end()), temp.end());
  int tlen = temp.size(); 
  for(int i = 0; i < len; i++) {
  	double nx = v[i].x, ny = v[i].y;
  	int tx = 0, ty = 0;
  	for(int j = 0; j < tlen; j++) if(abs(nx-temp[j]) <= EPS) { tx = j; break; }
  	for(int j = 0; j < tlen; j++) if(abs(ny-temp[j]) <= EPS) { ty = j; break; }
	for(int j = tx; j < ty; j++) sign[j] = true;
  }
  len = temp.size();
  for(int i = len-1; i > 0; i--) if(!sign[i-1]) return temp[i];
  return 0;
}

void output() {
  cout << fixed << showpoint << setprecision(2) << 0.0 << " " << Hash(L) << " " << 1000.0 << " " << Hash(R) << endl;
}

int main() {
  int n;
  while(getD(n)) {
  	bool flag = false;
  	for(int i = 0; i < n; i++) {
  	  if(vis[i]) continue;
  	  sta.clear();
  	  des.clear();
  	  dfs(n, node[i].x, node[i].y, node[i].r, i);
  	  if(insert(sta, L)) { flag = true; break; } 
  	  if(insert(des, R)) { flag = true; break; }
    } 
    if(flag) continue;
    output();
  }
  
  return 0;
}

作者认为可以先简化一下,先判断是否有解,再考虑如何求出最靠北的位置。首先,可以把每个敌人抽象成一个圆,本题就变成了,正方形内有n个圆形障碍物,是否能从最左边走到最右边
下一步需要一点创造性思维:把正方形战场看成一个湖,障碍物看成踏脚石,如果可以从上边界走到下边界,沿途经过的障碍物就会把湖隔成左右两半,相互无法到达,即本题无解;另一方面,如果从上边界走不到下边界,虽然仍然可能会出现某些封闭区域,但一定可以从左边界的某个地方走到右边界
这样解的存在性只需一次DFS或BFS判联通即可,如何求出最北的进出位置呢,从上边界开始遍历,沿途检查与边界相交的圆。这些圆和左边界的交点中最靠南边的一个就是所求的最北进入位置,和右边界的最南交点就是所求的最北离开位置

其实就是说验证了解的存在性,那么我们就可以从与上边界相交的圆出发,遍历所有圆,那么其与左边界有边界交点的最南处就是理想的入出点,当然如果没有的化,那就是1000了,注意相切的情况也应该算到联通的情况

好了,到这里讲解基本结束,完结撒花,感谢陪伴

posted @   banyanrong  阅读(38)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示