$ACM$ 课第三次作业-搜索
\(ACM\) 课第三次作业-搜索
\(A.\ Sum\ It\ Up\)
题意
给定 \(t\) 和 \(n\) 个整数 \(x_{i}\),从 \(n\) 个整数中挑任意个数,使得它们的和为 \(t\)
输入格式
多组数据,\(n\leq 12,\ t\leq 1000,\ x_{i}\leq 100\)
输出格式
数字之间添 \(+\) 号,不考虑顺序(\(2 + 2 + 1\) 与 \(2 + 1 + 2\) 看作同一个),按照字典序列从大到小输出所有组合
题解
由于 \(n\) 很小,故考虑二进制枚举,然后 \(check\)
由于不考虑顺序,所以对于每一个枚举的情况都要用 \(map\) 记忆化一下,即 map<multiset<int>, int>
输出结果用字符串拼接,放到容器里面排序即可
\(code\)
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int, int>
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int MOD = 1e9 + 7;
const int N = 4e2 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
char c = getchar();
int ans = 0, f = 1;
while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
return ans * f;
}
int t, n, cnt, a[20];
set<string, greater<string> > st;
map<multiset<int>, int> mp;
bool check(int x)
{
int res = 0;
for(int i = 0; i < n; ++i) {
if(x & (1 << i)) res += a[i];
}
return res == t;
}
int main()
{
while(scanf("%d %d", &t, &n) && t && n) {
mp.clear(), st.clear();
for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
printf("Sums of %d:\n", t);
for(int i = 0; i < (1 << n); ++i) {
if(check(i)) {
string temp = "";
multiset<int> mlst;
for(int j = 0; j < n; ++j)
if(i & (1 << j)) temp += a[j] + '0', mlst.insert(a[j]);
if(!mp[mlst]) {
mp[mlst] = 1;
st.insert(temp);
}
}
}
int cnt = st.size();
if(!cnt) puts("NONE");
else {
for(auto i: st) {
int len = i.length();
for(int j = 0; j < len; ++j)
printf("%d%c", i[j] - '0', "+\n"[j == len - 1]);
}
}
}
return 0;
}
/*
4 6 4 3 2 2 1 1
5 3 2 1 1
400 12 50 50 50 50 50 50 25 25 25 25 25 25
0 0
*/
时间复杂度
\(O(2^{n}\cdot n)\),极限也就 \(2^{12}\cdot 12\)
\(B.\ Fling\)
题意
给定一个 \(7\times 8\) 的地图,上面分布着至多 \(12\) 个小球,每个小球可以向上下左右四个方向移动('U', 'L', 'R', 'D'
)
对于每一个小球,只有两种情况它无法移动
-
其当前移动方向上没有小球
-
其当前移动方向上紧挨着一个小球
小球会传递动能
- 对于每一个小球 \(i\),若它在移动过程中撞到另一个小球 \(j\),那么 \(i\) 会立刻停在 \(j\) 的前一个位置,而 \(j\) 继续按当前方向移动
- 若 \(j\) 与 另一个小球 \(k\) 紧邻,那么 \(j\) 不动,\(k\) 按当前方向移动,直到它掉落,以此类推
输入格式
多组数据
输出格式
输出 \(i,\ j,\ k\) ,分别代表小球的横坐标、纵坐标和移动方向
若有多个答案,按照字典序最小的输出
题解
这是一个游戏,玩一玩就能理解题意
对于计算机而言,最好的策略就是一步一步尝试
任取一个球,向四个方向搜索,若搜索成功,则更新图,继续搜索;若搜索失败,则回溯,把图复原,换方向继续搜索
\(dfs\) 实际上就是一个穷尽所有可能,搜索、回溯的过程
- 往下搜索更新状态
- 往上回溯复原状态
考虑可行性剪枝
用一个字符串维护答案,若当前得到的答案字符串已经比我们之前的答案大,那么没必要继续搜索,返回即可
注意开一个临时数组 \(temp\_g\) 存储地图,便于回溯的时候复原地图,但是 \(temp\_g\) 得设置成局部变量
\(code\)
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int, int>
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int MOD = 1e9 + 7;
const int N = 4e2 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
char c = getchar();
int ans = 0, f = 1;
while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
return ans * f;
}
const int n = 7, m = 8;
const int D1[] = {-1, 0, 0, 1};
const int D2[] = {0, -1, 1, 0};
const char D[] = {'U', 'L', 'R', 'D'};
int vis[20][20], caseNum;
char g[20][20];
string ans;
struct node
{
int x, y;
char d;
node() {}
node(int _x, int _y, char _d) {x = _x, y = _y, d = _d;}
};
bool check(int s, int t) {return s >= 0 && s < n && t >= 0 && t < m;}
void dfs(int num, string &str)
{
if(num == 1) {
if(ans != "" && ans > str || ans == "") ans = str;
return;
}
for(int i = 0; i < n; ++i) {
for(int j = 0; j < m; ++j) {
if(g[i][j] == 'O') {
for(int k = 0; k < 4; ++k) {
if(check(i + D1[k], j + D2[k]) && g[i + D1[k]][j + D2[k]] == 'O') continue;
int f = 0;
for(int s = i + D1[k], t = j + D2[k]; check(s, t); s += D1[k], t += D2[k]) {
if(g[s][t] == 'O') {
f = 1;
break;
}
}
if(!f) continue;
else {//进入这个分支,就保证了从 i, j 往 k 方向移动球,一定可以移动
char temp_g[20][20] = {0}; //这个 temp 不能设为全局变量,否则会出错
memcpy(temp_g, g, sizeof(g));
g[i][j] = 'X';
for(int s = i + D1[k], t = j + D2[k]; check(s, t); s += D1[k], t += D2[k]) {
if(g[s][t] == 'O') {
g[s - D1[k]][t - D2[k]] = 'O';
g[s][t] = 'X';
}
}
str += i + '0', str += j + '0', str += D[k];
//cout<<str<<endl;
if(ans != "" && str > ans) { // 可行性剪枝,字典序小的答案不继续搜索
for(int it = 0; it < 3; ++it) str.erase(--str.end());
return;
}
dfs(num - 1, str);
for(int it = 0; it < 3; ++it) str.erase(--str.end()); //删除迭代器比赋值更快一点
memcpy(g, temp_g, sizeof(temp_g));
}
}
}
}
}
return;
}
int main()
{
while(~scanf("%s", g[0])) {
for(int i = 1; i < n; ++i)
scanf("%s", g[i]);
int cnt = 0;
for(int i = 0; i < n; ++i)
for(int j = 0; j < m; ++j)
if(g[i][j] == 'O') ++cnt;
string res = "";
dfs(cnt, res);
if(caseNum) puts(""); //诡异的输出格式
printf("CASE #%d:\n", ++caseNum);
if(ans != "") {
int len = ans.length();
for(int i = 0; i < len; i += 3) {
putchar(ans[i]), putchar(' ');
putchar(ans[i + 1]), putchar(' ');
putchar(ans[i + 2]), puts("");
}
}
memset(g, 0, sizeof(g));
ans = "";
}
return 0;
}
后记
我一开始打算穷尽所有可能,把可行的答案字符串全部存起来,最后排序输出最小的
不幸得,代码 \(T\) 了
迫不得已考虑可行性剪枝,结果时间不仅减下来了,代码长度也缩减了很多
\(C.\ N\) 皇后
题意
在 \(N\times N\) 的棋盘上放置 \(N\) 个皇后,任意两个皇后需要满足
- 不在同一行
- 不在同一列
- 不处在与棋盘呈 \(45^{\circ}\) 角的直线上
输入格式
多组数据,\(N\leq 10\)
输出格式
输出方案数
题解
经典题,考虑 \(dfs\)
枚举行,用 \(col[i],\ d1[i],\ d2[i]\) 维护列,两个对角线
对于处在斜率为 \(-1\) 的同一条直线上的两个皇后 \(i_{1},\ j_{1},\ i_{2},\ j_{2}\),满足 \(i_{1} - j_{1} = i_{2} - j_{2}\)
对于处在斜率为 \(1\) 的同一条直线上的两个皇后 \(i_{1},\ j_{1},\ i_{2},\ j_{2}\),满足 \(i_{1} + j_{1} = i_{2} + j_{2}\)
用上述条件进行可行性剪枝即可
对于此题,先预处理出答案,然后回答查询
\(code\)
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int, int>
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%d%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int MOD = 1e9 + 7;
const int N = 4e2 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
char c = getchar();
int ans = 0, f = 1;
while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
return ans * f;
}
int n, col[20];
map<int, int> mp1, mp2;
void dfs(int x, int n, int &ans)
{
if(x == n + 1) {
ans++;
return;
}
for(int i = 1; i <= n; ++i) {
if(!col[i] && !mp1[i - x] && !mp2[i + x]) {
mp1[i - x] = mp2[i + x] = 1;
col[i] = 1;
dfs(x + 1, n, ans);
mp1[i - x] = mp2[i + x] = 0;
col[i] = 0;
}
}
}
int main()
{
int ans[20] = {0};
for(int i = 1; i <= 10; ++i) {
memset(col, 0, sizeof(col));
mp1.clear(), mp2.clear();
dfs(1, i, ans[i]);
}
while(scanf("%d", &n) && n) {
printf("%d\n", ans[n]);
}
return 0;
}
/*
1
8
5
0
*/
后记
这题一开始没有预处理,狂 \(T\)
\(D.\ Find\ a\ way\)
题意
给定一个 \(n\times m\) 的地图,上面分布了多个 \(KFC\),#
表示不可走,其他均表示可走,走一个单位耗时 \(11\) 分钟
两个小憨批 \(Y,\ M\) 约着在某个 \(KFC\) 相遇,假设他们同时出发,求他们的最短相遇时间
输入格式
多组数据,\(2\leq n,\ m\leq 200\)
输出格式
输出最短相遇时间
题解
以 \(Y,\ M\) 为起点分别作 \(1\) 次 \(bfs\),对每个 \(KFC\) 维护一个俩人相遇所耗费的时间,最后取最小值即可
\(code\)
#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int, int>
#define arrayDebug(a, l, r) for(int i = l; i <= r; ++i) printf("%f%c", a[i], " \n"[i == r])
typedef long long LL;
typedef unsigned long long ULL;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
const int DX[] = {0, -1, 0, 1, 0, -1, -1, 1, 1};
const int DY[] = {0, 0, 1, 0, -1, -1, 1, 1, -1};
const int MOD = 1e9 + 7;
const int N = 4e2 + 7;
const double PI = acos(-1);
const double EPS = 1e-6;
using namespace std;
inline int read()
{
char c = getchar();
int ans = 0, f = 1;
while(!isdigit(c)) {if(c == '-') f = -1; c = getchar();}
while(isdigit(c)) {ans = ans * 10 + c - '0'; c = getchar();}
return ans * f;
}
int n, m, dis[207][207], vis[207][207];
char g[207][207];
struct node
{
int x, y, step;
node() {}
node(int _x, int _y, int _step) {x = _x, y = _y, step = _step;}
};
void bfs(int s, int t)
{
queue<node> q;
q.push(node(s, t, 0));
vis[s][t] = 1;
while(!q.empty()) {
node temp = q.front(); q.pop();
if(g[temp.x][temp.y] == '@') dis[temp.x][temp.y] += temp.step;
for(int i = 1; i <= 4; ++i) {
int sx = temp.x + DX[i], sy = temp.y + DY[i];
if(sx >= 1 && sx <= n && sx >= 1 && sy <= m && g[sx][sy] != '#' && !vis[sx][sy]) {
q.push(node(sx, sy, temp.step + 1));
vis[sx][sy] = 1;
}
}
}
}
int main()
{
while(~scanf("%d %d", &n, &m)) {
memset(dis, 0, sizeof(dis));
for(int i = 1; i <= n; ++i)
scanf("%s", g[i] + 1);
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= m; ++j) {
if(g[i][j] == 'Y' || g[i][j] == 'M') memset(vis, 0, sizeof(vis)), bfs(i, j);
}
}
int ans = inf;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= m; ++j)
if(g[i][j] == '@') ans = min(ans, dis[i][j]);
printf("%d\n", ans * 11);
}
return 0;
}