ARC001
- 题意:给定 \(8\times 8\)的矩阵,让你求是否有八皇后问题放置解,如果有,则输出任意一个解。
- 题解:就很普通的八皇后问题,dfs回溯寻找,如果有解直接输出然后退出程序,若跑完还没退出,则无解。关键点在于如何dfs,就很普通的坐标移动dfs,用\(x\)和\(y\)移动回溯,判断则用标记数组,\(r[i]\)表示第几行有,\(c[i]\)表示第几列有,但是斜线较难不过仔细想想也能表示。
- 代码:
#include <iostream>
#include <map>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
const ll N = 1e3+9;
struct node {
int w, v;
}a[N];
int dp[N][N];
char change[1010];
char ma[N][N];
int row[N];
int col[N];
int l[N];
int r[N];
int L(int i, int j) {
return i + 8 - j;
}
int R(int i, int j) {
return i + j - 1;
}
bool ff = 1;
int n = 8, m = 8;
void dfs(int x, int y) {
if (y > m)y = 1, x++;
if (x > n) {
int cnt = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (ma[i][j] == 'Q')cnt++;
}
}
if (cnt == 8) {
for (int i = 1; i <= n; i++) {
cout << (ma[i] +1) <<endl;
}
exit(0);
}
return;
}
if (!r[R(x, y)] && !l[L(x, y)] && !row[x] && !col[y] && ma[x][y] == '.') {
r[R(x, y)] = 1;
l[L(x, y)] = 1;
row[x] = 1;
col[y] = 1;
ma[x][y] = 'Q';
dfs(x, y + 1);
ma[x][y] = '.';
r[R(x, y)] = 0;
l[L(x, y)] = 0;
row[x] = 0;
col[y] = 0;
}
dfs(x, y+1);
}
void solve() {
for (int i = 1; i <= n; i++) {
cin >> (ma[i] + 1);
}
ff = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <=m; j++) {
if (ma[i][j] == 'Q') {
if (r[R(i, j)])ff = 0;
if (l[L(i, j)])ff = 0;
if (row[i])ff = 0;
if (col[j])ff = 0;
col[j] = 1;
row[i] = 1;
l[L(i, j)] = 1;
r[R(i, j)] = 1;
}
}
}
if (ff) {
dfs(1, 1);
}
cout <<"No Answer\n";
}
int main() {
int t = 1;
while (t--) {
solve();
}
}
- 题意:在网格图中给出起点和终点,求从起点到终点最短的距离,限制是这个类似跑道,给出\(2n\)个点,然后要从第\(0\)层跑到第\(n\)层,每次给两个点,只能在两点之间跑。
- 题解:以起点为起始点,然后维护两条线段\(l\)和\(r\),这两条线段是以起点为起始点,然后\(l\)和\(r\)的终点分别是能以直线到达跑道端点的最左端点和最右端点,即想象把\(l\)和\(r\)一直往两边倾斜碰到一点即停。然后,一层层得往下找,如果到了一层,两条线交叉了,说明要么到达的这一层,右边的端点在当前原点的\(l\)线的左侧,很显然如果在左侧,必然是让当前原点加上\(l\)改变当前原点的位置是最优的,如果你往中间走或者走\(r\)都是多走路,如果走弯曲的更是迷惑行为;要么到达的层左边的端点在当前原点的\(r\)线的右侧,同理让当前原点加上线段\(r\),答案即是每次更新时所加的线段的长度,最后改变终点坐标限制,左右端点都改成\((ex, n + 1)\),因为最后只能向着终点走,端点即无用。并且上述遍历结束,当前点一定是直接可以和终点相连,所以直接加上长度即可。
- 代码:
#include <iostream>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 3e5 + 99;
struct Vector {
double x, y;
Vector (int xx, int yy) : x(xx), y (yy) {}
Vector(){}//
double operator * (Vector rhs)const {//叉乘比较方便
return x * rhs.y-y * rhs.x;
}
Vector operator + (Vector rhs)const {
return Vector(x + rhs.x, y + rhs.y);
}
Vector operator - (Vector rhs)const {
return Vector(x - rhs.x, y - rhs.y);
}
double d() {
return sqrt ( x * x + y * y);
}
void pr() {
cout << x << " " << y << endl;
}
}l[N], r[N];
int main() {
int n;
cin >> n;
int sx, ex;
cin >> sx >> ex;
for (int i = 0; i <= n; i++) {
cin >> l[i].x >> r[i].x;
l[i].y = 1.0 * i;
r[i].y = 1.0 * i;
}
l[n].x = r[n].x = ex;//终点的要改一下
int i ,j = 0;
Vector cur(sx, 0), li, ri;
double ans= 0;
while (j + 1 <= n) {
j = cur.y + 1;
i = j + 1;
li = l[j] - cur;//所要维护的l线 和r线
ri = r[j] - cur;
while (j + 1<= n) {
bool changeL = 0;
if (li * (l[i] - cur) < 0)li = l[i] - cur, changeL = 1;//叉乘是右手握在纸面上是正
if (ri * (r[i] - cur) > 0)ri = r[i] - cur;//l线和r线向两边撕扯的过程
if (li * ri > 0) {//判断方向有没有出问题
if (changeL) {
ans += ri.d();
cur = cur + ri;
} else {
ans += li.d();
cur = cur + li;
}
break;
}
j++;
i++;
}
}
ans += (cur - l[n]).d();//最后加上答案
printf("%.15lf\n", ans);
return 0;
}