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了,注意相切的情况也应该算到联通的情况
好了,到这里讲解基本结束,完结撒花,感谢陪伴
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)