每日总结-1月13日


搜索进阶

dfs&bfs&迭代加深&A*算法

今日战况

Standing

部分题目题解

B题: Sticks

题目传送门

题目

George took sticks of the same length and cut them randomly until all parts became at most 50 units long. Now he wants to return sticks to the original state, but he forgot how many sticks he had originally and how long they were originally. Please help him and design a program which computes the smallest possible original length of those sticks. All lengths expressed in units are integers greater than zero.

Input

The input contains blocks of 2 lines. The first line contains the number of sticks parts after cutting, there are at most 64 sticks. The second line contains the lengths of those parts separated by the space. The last line of the file contains zero.

Output

The output should contains the smallest possible length of original sticks, one per line.

Sample Input

1
2
3
4
5
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

Sample Output

1
2
6
5
题目大意

大致就是给你n根长度小于50的木棍,要把他们拼成若干根相同长度的原始木棍,求这个长度的最小值

思路

n<65,考虑从小到大枚举每一种可能的原始木棍长度len,显然,len应该是所有木棍长度总和sum的约数(长度只能为整数),并且原始木棍的根数就应该是sum/len,然后对于每一种len,我们依次搜索每根原始木棍由哪些分散的木棍拼接而成即可.

加上各种优化

  1. 优化搜索顺序

    考虑到长度短的木棍比起长度长的木棍来说,拼接更为灵活,因此对木棍长度进行排序,从长到短搜索

  2. 排除等效冗余

    • 先拼上 x 和先拼上 y 是等效的,只需要搜索一次这种情况
    • 记录最近一次尝试拼接的小木棍的长度,如果分支搜索失败的话,不再尝试其他相同长度的木棍(相同长度的木棍都是等效的,该分支失败意味着所有相同长度的木棍必定会拼接失败)
    • 如果在拼接某个原始木棍时拼接第一根木棍的搜索分支就以失败返回,直接判定该分支无解,如果拼接第一根木棍就失败了的话,说明这根木棍拼不成长度为len的原始木棍,而如果继续搜索其他长度的小木棍的话,为了拼成所有的原始木棍,这根小木棍在后面是必须要用到的,如果它拼接失败,拼在其他原始木棍上也一定失败,不用继续往下搜索,因此可以直接判定无解
  3. 其他优化

    使用二分查找的方式来寻找解(代码中未使用)

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#define Inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int> P;
typedef long long LL;
const int MAXX=105;
const double eps=0.0000001;

int n,a[MAXX],st,en,num,lenn;
bool vis[MAXX];

bool dfs(int now,int len,int id){
if(now>num) return true;
if(len==lenn) return dfs(now+1,0,0);
int temp=0;
for(int i=id+1;i<=n;++i){
if(!vis[i]&&len+a[i]<=lenn&&a[i]!=temp){
vis[i]=true;
if(dfs(now,len+a[i],id))
return true;
temp=a[i];
vis[i]=false;
if(len==0) return false;
}
}
return false;
}

bool cmp(int jj,int kk){return jj>kk;}

void solve_it(){
st=0;en=0;
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
st=max(st,a[i]);
en+=a[i];
}
sort(a+1,a+n+1,cmp);

int ans;
for(int i=st;i<=en;++i){
if(en%i) continue;
num=en/i;lenn=i;
ans=i;
for(int j=1;j<=n;++j)
vis[j]=false;
if(dfs(1,0,0)) break;
}
printf("%d\n",ans);
}

int main(){
// int t;scanf("%d",&t);
// while(t--)
while(~scanf("%d",&n)&&n)
solve_it();

return 0;
}

G题: Addition Chains

题目传送门

题目

An addition chain for n is an integer sequence with the following four properties:
a_0 = 1
a_m = n
a_0 < a_1 < a_2 < … < a_m-1 < a_m
For each k (1<=k<=m) there exist two (not necessarily different) integers i and j (0<=i, j<=k-1) with a_k=a_i+a_j
You are given an integer n. Your job is to construct an addition chain for n with minimal length. If there is more than one such sequence, any one is acceptable.
For example, <1,2,3,5> and <1,2,4,5> are both valid solutions when you are asked for an addition chain for 5.

Input

The input will contain one or more test cases. Each test case consists of one line containing one integer n (1<=n<=100). Input is terminated by a value of zero (0) for n.

Output

For each test case, print one line containing the required integer sequence. Separate the numbers by one blank.
Hint: The problem is a little time-critical, so use proper break conditions where necessary to reduce the search space.

Sample Input

1
2
3
4
5
6
5
7
12
15
77
0

Sample Output

1
2
3
4
5
1 2 4 5
1 2 4 6 7
1 2 4 8 12
1 2 4 5 10 15
1 2 4 8 9 17 34 68 77
题目大意

有一个严格递增的序列,a_0=1,且对于每一个k(k>=1),存在两个可以相同也可以不同的数i,j(0<=i,j<=k-1),使得a_k=a_i+a_j

输入一个数n,求能得到数字n的最短的该序列

思路

使用迭代加深的思路,逐步加深搜索的层数,再利用贪心,每次都倒序进行搜索,即从k-1搜索到1,直到得到结果即为答案

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<iostream>
#include<string>
#define Inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int> P;
typedef long long LL;
const int MAXX=20;
const double eps=0.0000001;

int n,a[MAXX],depth;
bool has;

void output(){
for(int i=1;i<MAXX;++i){
if(a[i]==n){
printf("%d\n",a[i]);
break;
}
printf("%d ",a[i]);
}
}

void dfs(int now){
if(now>depth)
return;
for(int i=now-1;i>=1;--i){
for(int j=i;j>=1;--j){
a[now]=a[i]+a[j];
if(a[now]<=a[now-1]) break;
if(a[now]==n){
output();
has=true;return;
}
dfs(now+1);
if(has) return;
}
if(has) return;
}
}

void solve_it(){
if(n==1){
output();
return;
}

has=false;
for(int i=2;i<=12;++i){
depth=i;
dfs(2);
if(has) break;
}
}

int main(){
// int t;scanf("%d",&t);
// while(t--)
a[1]=1;
while(~scanf("%d",&n)){
if(n==0) break;
solve_it();
}

return 0;
}

H题: 打开灯泡 Switch the Lamp On

题目传送门

题目就不放了,比较简单,就是一个bfs求最短路,不过是双向队列的bfs,和用优先队列一样的,感觉就是建图有点麻烦

就直接放代码了

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<iostream>
#include<string>
#define Inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int> P;
typedef long long LL;
const int MAXX=505;
const double eps=0.0000001;

struct rode{
int tox,toy,len;
bool vis;
};
struct point{
int x,y,len;
};
int n,m,n1,m1,ans;
char s[MAXX][MAXX];
vector<rode> ro[MAXX][MAXX];

void bfs(){
deque<point> qq;
point pt;pt.x=1;pt.y=1;pt.len=0;
qq.emplace_front(pt);
while(!qq.empty()){
point pp=qq.front();qq.pop_front();
if(pp.x==n1&&pp.y==m1){
ans=pp.len;
break;
}
int si=ro[pp.x][pp.y].size();
for(int i=0;i<si;++i){
if(!ro[pp.x][pp.y][i].vis){
ro[pp.x][pp.y][i].vis=true;
pt.x=ro[pp.x][pp.y][i].tox;
pt.y=ro[pp.x][pp.y][i].toy;
pt.len=pp.len+ro[pp.x][pp.y][i].len;
if(ro[pp.x][pp.y][i].len==0)
qq.emplace_front(pt);
else
qq.emplace_back(pt);
}
}
}

}

void solve_it(){
scanf("%d%d",&n,&m);
n1=n+1;m1=m+1;
for(int i=1;i<=n;++i){
getchar();
scanf("%s",s[i]+1);
}

ans=Inf;
rode temp;temp.vis=false;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
temp.len=(s[i][j]=='\\');
temp.tox=i;temp.toy=j+1;
ro[i+1][j].emplace_back(temp);
temp.tox=i+1;temp.toy=j;
ro[i][j+1].emplace_back(temp);
temp.len=1-temp.len;
temp.tox=i+1;temp.toy=j+1;
ro[i][j].emplace_back(temp);
temp.tox=i;temp.toy=j;
ro[i+1][j+1].emplace_back(temp);
}

bfs();

if(ans==Inf)
printf("NO SOLUTION\n");
else
printf("%d\n",ans);
}

int main(){
// int t;scanf("%d",&t);
// while(t--)
solve_it();

return 0;
}

J题: Bloxorz l

题目传送门

一个比较繁琐的bfs求最短路题目

题目

Little Tom loves playing games. One day he downloads a little computer game called ‘Bloxorz’ which makes him excited. It’s a game about rolling a box to a specific position on a special plane. Precisely, the plane, which is composed of several unit cells, is a rectangle shaped area. And the box, consisting of two perfectly aligned unit cube, may either lies down and occupies two neighbouring cells or stands up and occupies one single cell. One may move the box by picking one of the four edges of the box on the ground and rolling the box 90 degrees around that edge, which is counted as one move. There are three kinds of cells, rigid cells, easily broken cells and empty cells. A rigid cell can support full weight of the box, so it can be either one of the two cells that the box lies on or the cell that the box fully stands on. A easily broken cells can only support half the weight of the box, so it cannot be the only cell that the box stands on. An empty cell cannot support anything, so there cannot be any part of the box on that cell. The target of the game is to roll the box standing onto the only target cell on the plane with minimum moves.

The box stands on a single cell

The box stands on a single cell ↑

The box lies on two neighbouring cells, horizontally

The box lies on two neighbouring cells, horizontally ↑

The box lies on two neighbouring cells, vertically

The box lies on two neighbouring cells, vertically ↑

After Little Tom passes several stages of the game, he finds it much harder than he expected. So he turns to your help.

Input

Input contains multiple test cases. Each test case is one single stage of the game. It starts with two integers R and C(3 ≤ R, C ≤ 500) which stands for number of rows and columns of the plane. That follows the plane, which contains R lines and C characters for each line, with ‘O’ (Oh) for target cell, ‘X’ for initial position of the box, ‘.’ for a rigid cell, ‘#’ for a empty cell and ‘E’ for a easily broken cell. A test cases starts with two zeros ends the input.

It guarantees that

  • There’s only one ‘O’ in a plane.
  • There’s either one ‘X’ or neighbouring two ‘X’s in a plane.
  • The first(and last) row(and column) must be ‘#’(empty cell).
  • Cells covered by ‘O’ and ‘X’ are all rigid cells.

Output

For each test cases output one line with the minimum number of moves or “Impossible” (without quote) when there’s no way to achieve the target cell.

Sample Input

1
2
3
4
5
6
7
8
9
7 7
#######
#..X###
#..##O#
#....E#
#....E#
#.....#
#######
0 0

Sample Output

1
10
题目大意

有一个 1 * 1 * 2 大小的长方体,在一个棋盘上,有初始位置,有一个目标位置,问走几步能到达目标位置,棋盘上有几种方块地皮

  • ‘.’ 坚硬的地面,可以立在上面
  • ‘E’ 易碎的(玻璃?),不能立在上面,但是可以躺一半在上面
  • ‘#’ 上面不能存在东西,(虚空?)
  • ‘X’ 起始位置,可能是平躺的
  • ‘O’ 目标位置,只能是立着的

输入棋盘大小和整个棋盘,输出最少需要走几步能到目标位置。

思路

从起点开始bfs,和普通的dfs相比,就是麻烦,需要记录长方体的位置和状态(立着,横着平躺,竖着平躺),每个状态对应的下一步还都不一样。

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<cstring>
#include<string>
#include<set>
using namespace std;
typedef pair<int,int> P;
typedef long long LL;
const int Inf=0x3f3f3f3f;
const int MAXX=505;
const double eps=0.0000001;

struct point{
int x,y,kind;//0:立起来 1:竖着 2:横着
point(int xx=0,int yy=0,int kk=0):x(xx),y(yy),kind(kk){}
friend bool operator<(point jj,point kk){return jj.kind<kk.kind;}
};
struct PP{
point pos;
int len;
friend bool operator<(PP jj,PP kk){
return jj.len>kk.len;
}
};
int n,m,ans;
char a[MAXX][MAXX],vis[MAXX][MAXX][4];

void bfs(){
int enx=0,eny=0;
queue<PP> pq;
point p;PP pp;
pp.len=0;
bool findst=false,finden=false;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
if(!findst&&a[i][j]=='X'){
findst=true;
pp.pos.x=i;pp.pos.y=j;
bool two=false;
if(a[i+1][j]=='X'){
two=true;
pp.pos.kind=1;
}
else if(a[i][j+1]=='X'){
two=true;
pp.pos.kind=2;
}
if(!two){
pp.pos.kind=0;
}
pq.push(pp);
vis[pp.pos.x][pp.pos.y][pp.pos.kind]=true;
}
if(!finden&&a[i][j]=='O'){
finden=true;
enx=i;eny=j;
}
}
if(findst&&finden) break;
}

//bfs
while(!pq.empty()){
PP now=pq.front();pq.pop();
int i=now.pos.x,j=now.pos.y;
if(now.pos.kind==0){
if(now.pos.x==enx&&now.pos.y==eny){
ans=now.len;
break;
}
if(i-2>=1)
if(a[i-1][j]!='#'&&a[i-2][j]!='#'){
p.kind=1;p.x=i-2;p.y=j;
if(!vis[p.x][p.y][p.kind]){
vis[p.x][p.y][p.kind]=true;
pp.pos=p;pp.len=now.len+1;
pq.push(pp);
}
}
if(i+2<=n)
if(a[i+1][j]!='#'&&a[i+2][j]!='#'){
p.kind=1;p.x=i+1;p.y=j;
if(!vis[p.x][p.y][p.kind]){
vis[p.x][p.y][p.kind]=true;
pp.pos=p;pp.len=now.len+1;
pq.push(pp);
}
}
if(j-2>=1)
if(a[i][j-1]!='#'&&a[i][j-2]!='#'){
p.kind=2;p.x=i;p.y=j-2;
if(!vis[p.x][p.y][p.kind]){
vis[p.x][p.y][p.kind]=true;
pp.pos=p;pp.len=now.len+1;
pq.push(pp);
}
}
if(j+2<=m)
if(a[i][j+1]!='#'&&a[i][j+2]!='#'){
p.kind=2;p.x=i;p.y=j+1;
if(!vis[p.x][p.y][p.kind]){
vis[p.x][p.y][p.kind]=true;
pp.pos=p;pp.len=now.len+1;
pq.push(pp);
}
}
}
else if(now.pos.kind==1){
if(i-1>=1)
if(a[i-1][j]!='#'&&a[i-1][j]!='E'){
p.kind=0;p.x=i-1;p.y=j;
if(!vis[p.x][p.y][p.kind]){
vis[p.x][p.y][p.kind]=true;
pp.pos=p;pp.len=now.len+1;
pq.push(pp);
}
}
if(i+2<=n)
if(a[i+2][j]!='#'&&a[i+2][j]!='E'){
p.kind=0;p.x=i+2;p.y=j;
if(!vis[p.x][p.y][p.kind]){
vis[p.x][p.y][p.kind]=true;
pp.pos=p;pp.len=now.len+1;
pq.push(pp);
}
}
if(j-1>=1)
if(a[i][j-1]!='#'&&a[i+1][j-1]!='#'){
p.kind=1;p.x=i;p.y=j-1;
if(!vis[p.x][p.y][p.kind]){
vis[p.x][p.y][p.kind]=true;
pp.pos=p;pp.len=now.len+1;
pq.push(pp);
}
}
if(j+1<=m)
if(a[i][j+1]!='#'&&a[i+1][j+1]!='#'){
p.kind=1;p.x=i;p.y=j+1;
if(!vis[p.x][p.y][p.kind]){
vis[p.x][p.y][p.kind]=true;
pp.pos=p;pp.len=now.len+1;
pq.push(pp);
}
}
}
else if(now.pos.kind==2){
if(i-1>=1)
if(a[i-1][j]!='#'&&a[i-1][j+1]!='#'){
p.kind=2;p.x=i-1;p.y=j;
if(!vis[p.x][p.y][p.kind]){
vis[p.x][p.y][p.kind]=true;
pp.pos=p;pp.len=now.len+1;
pq.push(pp);
}
}
if(i+1<=n)
if(a[i+1][j]!='#'&&a[i+1][j+1]!='#'){
p.kind=2;p.x=i+1;p.y=j;
if(!vis[p.x][p.y][p.kind]){
vis[p.x][p.y][p.kind]=true;
pp.pos=p;pp.len=now.len+1;
pq.push(pp);
}
}
if(j-1>=1)
if(a[i][j-1]!='#'&&a[i][j-1]!='E'){
p.kind=0;p.x=i;p.y=j-1;
if(!vis[p.x][p.y][p.kind]){
vis[p.x][p.y][p.kind]=true;
pp.pos=p;pp.len=now.len+1;
pq.push(pp);
}
}
if(j+2<=m)
if(a[i][j+2]!='#'&&a[i][j+2]!='E'){
p.kind=0;p.x=i;p.y=j+2;
if(!vis[p.x][p.y][p.kind]){
vis[p.x][p.y][p.kind]=true;
pp.pos=p;pp.len=now.len+1;
pq.push(pp);
}
}
}
}
}

void solve_it(){
for(int i=1;i<=n;++i){
getchar();
scanf("%s",a[i]+1);
}

ans=-1;
bfs();

if(ans==-1)
printf("Impossible\n");
else
printf("%d\n",ans);

for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
for(int k=0;k<=2;++k)
vis[i][j][k]=false;
}

int main(){
// int t;scanf("%d",&t);
// while(t--)
while(~scanf("%d%d",&n,&m)){
if(n==0&&m==0) break;
solve_it();
}

return 0;
}

总结

搜索的优化还是比较难的,优化永无止境。


文章作者: Shaun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Shaun !
评论
  目录