A题是八数码,每次交换x和旁边的数字,求怎么交换能最后变成1 2 3 4 5 6 7 8 x的形式
用正常的BFS搜索+康托展开判重
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <queue>
#include <string.h>
using namespace std;
int vis[400000], a[3][3];
int dx[] = {0,1,0,-1};
int dy[] = {1,0,-1,0};
char dir[] = {'r','d','l','u'};
int ed[3][3], flag1 = 0, x_x, y_x;
long long fac[]= {1,1,2,6,24,120,720,5040,40320,362880}; //阶乘表
//康托展开
long long cantor(int a[][3])
{
long long temp, num = 0;
int i, j;
for(i = 0; i < 9; i++)
{
temp = 0;
for(j = i + 1; j < 9; j++)
{
if(a[j / 3][j % 3] < a[i / 3][i % 3]) temp++;
//判断几个数小于它
}
num += fac[8 - i] * temp;
}
return num;
}
long long cal(int a[][3])
{
long long res;
res = a[0][0] * 1e8 + a[0][1] * 1e7 + a[0][2] * 1e6 + a[1][0] * 1e5 + a[1][1] * 1e4 + a[1][2] * 1e3 + a[2][0] * 1e2 + a[2][1] * 10 + a[2][2];
return res;
}
struct node
{
int s[3][3];
int ans;
int step;
char path[111];
}now, next;
queue<node> q;
int check(int s[][3])
{
int i, j;
int flag = 0;
for(i = 0; i < 3; i++)
{
for(j = 0; j < 3; j++)
{
if(now.s[i][j] != ed[i][j]) flag = 1;
}
}
return flag;
}
void bfs(int a[][3])
{
while(!q.empty()) q.pop();
int i, j, xx = x_x, yy = y_x;
for(i = 0; i < 3; i++)
{
for(j = 0; j < 3; j++)
{
now.s[i][j] = a[i][j];
}
}
now.ans = cantor(a);
vis[now.ans] = 1;
now.step = -1;
q.push(now);
while(!q.empty())
{
now = q.front();
q.pop();
if(now.ans == 46233)
{
puts(now.path);
return;
}
for(i = 0; i < 3; i++)
{
for(j = 0; j < 3; j++)
{
if(now.s[i][j] == 0) {xx = i; yy = j;}
}
}
//记录此时"x"的位置
for(i = 0; i < 4; i++)
{
//四个方向拓展
int x1, y1;
x1 = xx + dx[i];
y1 = yy + dy[i];
if(x1 < 0 || x1 > 2 || y1 < 0 || y1 > 2) continue;
next = now;
next.s[xx][yy] = now.s[x1][y1];
next.s[x1][y1] = 0;
next.ans = cantor(next.s);
next.step = now.step + 1;
next.path[next.step] = dir[i];
if(vis[next.ans])
{
continue;
//康托展开判重
}
if(next.ans == 46233)
{
puts(next.path);
return;
}
vis[next.ans] = 1;
q.push(next);
}
}
printf("unsolvable\n");
}
int main()
{
char s[2], i;
int xx, yy;
ed[0][0] = 1;
ed[0][1] = 2;
ed[0][2] = 3;
ed[1][0] = 4;
ed[1][1] = 5;
ed[1][2] = 6;
ed[2][0] = 7;
ed[2][1] = 8;
ed[2][2] = 0;
for(i = 0; i < 9; i++)
{
scanf("%s", s);
int x;
if(s[0] == 'x') {x = 0; x_x = i / 3; y_x = i % 3;}
else x = s[0] - '0';
xx = i / 3;
yy = i % 3;
a[xx][yy] = x;
}
memset(vis, 0, sizeof(vis));
bfs(a);
return 0;
}
//2 3 4 1 5 x 7 6 8
//1 2 3 4 x 6 7 5 8
/*
2 3 4
1 5 x
7 6 8
*/
B题是一个BFS,在一个矩阵里走日字,从起点到终点最少多少步
BFS的模板还是很万能的
这里注意一下输入,“e1 e2”要分两个输,给出的两种都是可以的,但是不能用while(*gets(s)!=EOF) *gets(s)表示s[0],而gets读到EOF的时候返回的是NULL,用*取NULL里的值就RE了while(gets(s)!=NULL)
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <queue>
using namespace std;
struct kkk
{
int x;
int y;
int step;
};
int dx[] = {2, 2, 1, 1, -1, -1, -2, -2};
int dy[] = {1, -1, 2, -2, 2, -2, 1, -1};
//走日字的方向
int vis[100][100];
queue<kkk> q;
bool check(int x, int y)
{
if(x < 1 || y < 1 || x > 8 || y > 8) return 0;
else if(vis[x][y] == 1) return 0;
else return 1;
}
int bfs(int a, int b, int aa, int bb)
{
while(!q.empty()) q.pop();
kkk now, next;
now.x = a;
now.y = b;
now.step = 0;
q.push(now);
if(now.x == aa && now.y == bb) return 0;
memset(vis, 0, sizeof(vis));
vis[now.x][now.y] = 1;
while(!q.empty())
{
now = q.front(); q.pop();
int i;
for(i = 0; i < 8; i++)
{
next.x = now.x + dx[i];
next.y = now.y + dy[i];
if(check(next.x, next.y))
{
vis[next.x][next.y] = 1;
next.step = now.step + 1;
q.push(next);
if(next.x == aa && next.y == bb) return next.step;
}
}
}
return next.step;
}
int main()
{
char s[10], c1, c2;
int a1, a2;
// while(~scanf(" %c%d %c%d", &c1, &a1, &c2, &a2))//前面有一个空格 嗯
// {
// int sx = c1 - 96;
// int ex = c2 - 96;
// int res;
// res = bfs(sx, a1, ex, a2);
// printf("To get from %c%d to %c%d takes %d knight moves.\n", c1, a1, c2, a2, res);
// }
while(~scanf("%s%s",s,s+3))
{
int sx = s[0] - 96, sy = s[1] - '0';
int ex = s[3] - 96, ey = s[4] - '0';
int res;
res = bfs(sx, sy, ex, ey);
printf("To get from %c%c to %c%c takes %d knight moves.\n", s[0], s[1], s[3], s[4], res);
}
return 0;
}
C题是一个LIS,寻找前一位递增后一位递减的最长序列
On2的,用一个pre数组记录路径,这个数组就相当于一个指针,然后最后逆序输出
也有一点点模板的影子
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
struct kkk
{
int w;
int s;
int num;
} m[1005];
bool cmp(kkk a, kkk b)
{
if(a.w != b.w) return a.w < b.w;
return a.s > b.s;
}
using namespace std;
int f[1005], pre[1005], res[1005];
int main()
{
int n = 1;
int i, j, ans = 0, p = 0;
while(~scanf("%d %d", &m[n].w, &m[n].s))
{
n++;
m[n].num = n;
f[n] = 1;
}
n--;//这么记录n会多一个,虽然没n--也ac了……
sort(m + 1, m + 1 + n, cmp);
//printf("%d\n", n);
for(i = 1; i <= n; i++)
{
for(j = 1; j <= i; j++)
{
if(m[j].w < m[i].w && m[j].s > m[i].s)
{
if(f[i] < f[j] + 1)
{
f[i] = f[j] + 1;
pre[i] = j;//记录转移路径
}
}
}
if(f[i] > ans)
{
ans = f[i];
p = i;
}
}
printf("%d\n", ans);
i = p;
j = 0;
while(i > 0)
{
res[j] = m[i].num;
j++;
i = pre[i];
}
for(i = j - 1; i >= 0; i--)
{
printf("%d\n", res[i]);
}
return 0;
}
xxx【这是dfs之前做的事情】
dfs();【递归】
xxx【这是一层dfs做完后回溯做的事情】
wa了很久很久,发现dfs的起始状态写错了……
#include <stdio.h>
#include <stdlib.h>
/*
做完i题做j题需要arr[i][j]时间,做题的时间要求递增(可以相等),问最多做多少道题
这是一道DFS题
用到了回溯
xxx【这是dfs之前做的事情】
dfs();【递归】
xxx【这是一层dfs做完后回溯做的事情】
*/
/*
当时wa了好久,第一次递归dfs(0, 0, 1);
第一个0不是arr[0][0],是第一个做第0题==
*/
int vis[20], arr[18][18];
int n, res = 1;
int check(int last, int now, int t)
{
if(arr[last][now] < t || vis[now] == 1) return 0;
else return 1;
}
/*last->上一个做的第last题
t->做上一个题用了t时间
ans->现在总共做了ans个题
*/
void dfs(int last, int t,int ans)
{
int now;
vis[last] = 1;
for(now = 1; now < n; now++)
{
if(check(last, now, t))
{
vis[now] = 1;
ans++;
if(ans > res) res = ans;
dfs(now, arr[last][now], ans);
vis[now] = 0;
ans--;
//一层递归做完 回溯
}
}
}
int main()
{
while(~scanf("%d", &n))
{
int i, j;
res = 1;
for(i = 0; i < n; i++)
{
for(j = 0; j < n; j++)
{
scanf("%d", &arr[i][j]);
}
vis[i] = 0;
}
dfs(0, 0, 1);
printf("%d\n", res);
}
return 0;
}
最左端就是它起始位置和终止位置(就是这个数的大小)的min
最右端就是它的起始位置加上它右面有多少个数比它小(比它小的数在每次冒泡的时候要左移,相应这个数就要往右移)
数据到1e5,所以要用归并,复杂度为nlogn
归并带递归的,大佬给讲了很久才听懂==
归并就是把大区间一次一次分成两半,分到最后每一半都只有一个之后开始归并,两段归并排好序之后合起来,再和下一段归并,这样归并的区间越来越长,最后就把整个区间处理好了
归并的最初目的其实是排序,但是后来发现在归并过程中可以统计一些信息,就比如这道题的逆序数
归并的过程还要好好理解一下
小注:warning“Implicit declaration of function”“conflicting types for merge”,把merge函数写在了后面,sort函数里出现了merge函数,就要在sort前面写merge
#include <stdio.h>
#include <stdlib.h>
int a[100005], l[100005], r[100005], cnt[100005], tmp[100005], num[100005];
int k;
int min(int a, int b)
{
return b>a?a:b;
}
void merge(int first, int mid, int last)
{
int i, j;
if(last - first < 1) return;
i = first, j = mid + 1;
k = 1;
//cnt 前面有多少比它大的
while(i <= mid && j <= last)
{
if(a[i] < a[j])
{
tmp[k] = a[i];
k++;
i++;
}
else //a[i] > a[j]
{
//cnt[a[j]] += mid - first + 1 - i + 1;
cnt[a[j]] += mid + 1 - i;
tmp[k] = a[j];
k++;
j++;
}
}
while(i <= mid)
{
tmp[k] = a[i];
k++;
i++;
}
while(j <= last)
{
tmp[k] = a[j];
k++;
j++;
}
for(i = 1; i <= last - first + 1; i++)
{
a[first + i - 1] = tmp[i];
//printf("%d ", tmp[i]);
}
//printf("\n");
}
void sort(int first, int last)
{
if(first < last)
{
int mid = (first + last) / 2;
sort(first, mid);
sort(mid + 1, last);
merge(first, mid, last);
}
}
int main()
{
int T, t;
scanf("%d", &T);
for(t = 1; t <= T; t++)
{
int n, i;
k = 1;
scanf("%d", &n);
for(i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
l[a[i]] = min(a[i], i);
cnt[i] = 0;
num[a[i]] = i;
}
sort(1, n);
printf("Case #%d:", t);
for(i = 1; i <= n; i++)
{
//cnt[i] = i - 1 - (num[i] - 1 - cnt[i]);
//cnt[i] = i - num[i] + cnt[i];
//r[i] = num[i] + cnt[i];
r[i] = i + cnt[i];
//printf("%d %d %d\n", r[i], l[i], cnt[i]);
printf(" %d", r[i] - l[i]);
}
printf("\n");
}
return 0;
}
F就很水了,字符串处理,分类讨论
注意当出现“0/0”的时候会输出奇怪的东西
太简单就不粘代码了