# ZJUT2021 first search in the second semester [2]

Posted by stirton on Fri, 11 Feb 2022 23:28:35 +0100

E - POJ1915 Knight Moves:

Background
Mr Somurolov, fabulous chess-gamer indeed, asserts that no one else but him can move knights from one position to another so fast. Can you beat him?
The Problem
Your task is to write a program to calculate the minimum number of moves needed for a knight to reach one point from another, so that you have the chance to be faster than Somurolov.
For people not familiar with chess, the possible knight moves are shown in Figure 1.

INPUT:The input begins with the number n of scenarios on a single line by itself.
Next follow n scenarios. Each scenario consists of three lines containing integer numbers. The first line specifies the length l of a side of the chess board (4 <= l <= 300). The entire board has size l * l. The second and third line contain pair of integers {0, ..., l-1}*{0, ..., l-1} specifying the starting and ending position of the knight on the board. The integers are separated by a single blank. You can assume that the positions are valid positions on the chess board of that scenario.

OUTPUT:For each scenario of the input you have to calculate the minimal amount of knight moves which are necessary to move from the starting point to the ending point. If starting point and ending point are equal,distance is zero. The distance must be written on a single line.

 ```3 8 0 0 7 0 100 0 0 30 50 10 1 1 1 1``` ```5 28 0```
```#include<iostream>
#include<queue>
#include <utility>
#include<iomanip>
using namespace std;

int stepS[305][305];  //Record the number of steps taken from the starting point
int stepE[305][305];  //Record the number of steps taken from the end
int d[305][305];  //Record the access status of each point. 0 indicates that it has not been accessed, 1 indicates that it has been accessed from the starting point, and - 1 indicates the end point
int conS=1,conE=1;  //Record the number of points on the same layer, that is, the number of steps with the same number. Initially, there is only one starting point and one ending point
int l, sx, sy, ex, ey,ans;
typedef pair<int, int>p; p ps ,pe;  //Storing two-dimensional coordinates with pair template
queue<p>queS, queE;  //Two queues are set up to search from the starting point and the end point
int dx[8] = { 1,1,-1,-1,2,2,-2,-2 };
int dy[8] = { 2,-2,2,-2,1,-1,1,-1 };  //Eight ways to take one step

int ope(p pa,int k) {
int flag=0;
for (int i = 0; i < 8; i++) {  //List eight ways
int nx = pa.first + dx[i], ny = pa.second + dy[i];
if (nx >= 0 && nx < l && ny >= 0 && ny < l && d[nx][ny] == 0){//Is on the chessboard and has not been accessed
d[nx][ny] = k;
if (k == 1) {
stepS[nx][ny] = stepS[pa.first][pa.second] + 1;
queS.push(p(nx, ny));
conS++;  //Record the number of positions with the same number of steps
}else {
stepE[nx][ny] = stepE[pa.first][pa.second] + 1;
queE.push(p(nx, ny));
conE++;
}
}                                              //Inside the chessboard and accessed by the other end
else if (nx >= 0 && nx < l && ny >= 0 && ny < l && d[nx][ny] == -k) {
if (k == 1)stepS[nx][ny] = stepS[pa.first][pa.second] + 1;
else stepE[nx][ny] = stepE[pa.first][pa.second] + 1;
flag = stepS[nx][ny] + stepE[nx][ny];  //The final answer is the sum of the steps from both ends to this point
break;
}
}
return flag;  //When the two ends do not meet, the flag is always 0
}

int bfs() {
ps.first = sx; ps.second = sy;
pe.first = ex; pe.second = ey;
queS.push(ps);
queE.push(pe);
d[ps.first][ps.second] = 1;
d[pe.first][pe.second] = -1;
while (queS.size()|| queE.size()) {  //Both search queues are empty, the search ends
while(queS.size()&&(conS--)) {  //If the queue is not empty, further search the width of all points with the same minimum number of steps
ps = queS.front(); queS.pop();
ans=ope(ps,1); //1 means starting from the starting point, - 1 means starting from the end point
if (ans)return ans;  //Return results if any
}
while(queE.size()&&(conE--)) {  //The minimum number of further search steps of all queues is not the same
pe = queE.front(); queE.pop();
ans = ope(pe,-1);
if (ans)return ans;  //Return results if any
}
}
return 0;  //If there is still no result after the search, it means that the starting point cannot reach the end point, and 0 is returned
}
int main() {
int n;
cin >> n;
while (n--) {
cin >> l >> sx >> sy >> ex >> ey;
if (sx == ex && sy == ey)cout << 0 << endl;  //Special judgment for the same starting point and ending point
else {
cout << bfs() << endl;
for (int i = 0; i < l; i++)     //There are multiple groups of data, which can be initialized manually
for (int j = 0; j < l; j++) {
stepS[i][j] = stepE[i][j]= d[i][j] = 0;
}
while (queS.size())queS.pop();
while (queE.size())queE.pop();
}
}

}```

F - dfs pruning (POJ1011 Sticks): George took sticks of the same length and cut them randomly until all parts become 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.

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

```9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0```
```6
5
```
```#include<iostream>
#include<algorithm>
using namespace std;
int sum, total,n,l;  //n total number of rods, sum total length of rods. Each long rod with length of l can have total
int a[65],d[65];  //a [] record the length of each rod and d [] record the access status

bool cmp(int a, int b) {  //Comparison function of descending sort
return a > b;
}
//nl records the length of the long rod that has been rounded up at present
bool dfs(int s, int nl, int pos) {  //s records the number of long rods that have been scraped up, and pos records the access subscript
if (s == total - 1)return 1;  //Pruning, if only the last long stick is left, the remaining sticks will be merged automatically, and there is no need to make up
int former = -1;  //Record the length of the previous rod accessed, with an initial value of - 1
for (int i = pos; i < n; i++) {
if (d[i] == 1 || a[i] == former)continue;  //Pruning, directly skip the searched rod length
d[i] = 1;  //Access the bar with subscript i
if (nl + a[i] < l) {
if (dfs(s, nl + a[i], i))return 1;
else former = a[i];  //Record the failure lesson. At this time, the access stick has no result
}
else if(nl+a[i]==l){
if (dfs(s + 1, 0, 0))return 1;  //Add 1 to the number of sticks and visit from scratch
else former = a[i];  //Record the failure lesson. At this time, the access stick has no result
}
d[i] = 0;  //After the search for the stick with subscript i fails, it is traced back and recorded as no access
if (nl == 0)break;  //Pruning, when building a new rod with the rod labeled i below (because the array is in descending order, this should be currently available)
return 0;               //The longest stick used), if the combination of the sticks fails, it indicates that the current combination method cannot combine all the sticks
}                           //The rods are used, jump out of the loop and return failure

int main() {
while (cin >> n && n != 0) {
sum = 0;
bool flag = 0;
for (int i = 0; i < n; i++) {
cin >> a[i]; sum += a[i];  //Enter the length of each rod to get the total length
}
sort(a, a + n, cmp); //Prune, sort the bar length array in descending order, and then process it from the beginning. Give priority to the processing of long bars, and less needs to be considered
for (int i = a[0]; i <= sum/2; i++) { //Consider dividing it into two long rods
total = sum / i; l = i;
if (sum%i==0 && dfs(0,0,0)){  //Pruning, if the length of the long rod must be the factor of the total length
cout << i << endl;
flag = 1;
break;
}
}
if (flag==0)cout << sum << endl; //Special judgment, all rods are combined into a long rod
for (int i = 0; i < n; i++)d[i] = 0;  //Initialize the access status array;
}
}```

G - POJ2248 Addition Chain: an addition chain for n is an integer sequence < A0, A1, A2,..., am > with the following four properties:

1.a0 = 1;
2.am = n;
3.a0 < a1 < a2 < ... < am-1 < am;
4.For each k (1 <= k <= m) there exist two (not necessarily different) integers i and j (0 <= i, j <= k-1) with ak = ai + aj;
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.

5 7 12 15 77 0

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

```#include<iostream>
using namespace std;
int deep, n, tmp;  //deep record search depth, n record input
int finish, ans[100] = { 1 };  //finish record whether it is found and ans [] record the answer

void dfs(int cur ,int deep) {  //cur records the current depth, and deep is the search depth
if (cur == deep) {  //When the search reaches the target depth, if the answer is equal to n, the search ends
if (ans[cur] == n) {
finish = 1; return;
}
}
for (int i = 0; i <= cur; i++) {  //The search process matches each a[i] with itself and all numbers after itself
for (int j = i; j <= cur; j++) { //The new number must be larger than the current maximum number before a larger number appears, and
if (ans[i] + ans[j] > ans[cur] && ans[i] + ans[j] <= n) {  //Less than n
int sum = ans[i] + ans[j];
for (int k = cur + 1; k < deep; k++)
sum <<= 1; //*Simulate the result of self multiplying by 2 before the new number reaches the depth deep, which will be what can be produced by deep
if (sum < n)   //Generate the maximum number, because the new number must be the maximum number in the current sequence, if it reaches after several rounds
continue;  //Less than n, the answer must be wrong, skip directly.
ans[cur + 1] = ans[i] + ans[j];
dfs(cur + 1, deep);  //Depth + 1, continue searching;
if (finish)  //Search succeeded, return directly
return;
}
}
}
}

int main() {
while (cin >> n && n != 0) {
deep = 0; tmp = n; finish = 0;
while (tmp >>= 1)deep++; //Equivalent to 1 2 4 8 In the extreme case of deep, the thought is consistent with (*)
while (finish == 0)dfs(0, deep++);  //Iterate and deepen the search until the search is successful
for (int i = 0; i < deep; i++) {  //Output answer
cout << ans[i];
if (i != deep - 1)cout << " ";
}
cout << endl;
}
}```

H - half search (Luogu 4799 [CEOI2015 Day2] world hockey championship):

This year's world hockey championship was held in the Czech Republic. Bobek has arrived in Prague. He is not a fan of any team and has no sense of time. He just wants to watch a few games. If he had enough money, he would go to all the games. Unfortunately, his property was very limited. He decided to use all his property to buy tickets. Give Bobek's budget and the ticket price of each game. Try: if the total ticket price does not exceed the budget, how many viewing schemes does he have. If there is a game that is watched in one scheme and not in the other, the two schemes are considered to be different

INPUT:

In the first line, two positive integers ﹐ N ﹐ and ﹐ M(1 ≤ N ≤ 40,1 ≤ M ≤))(1≤N≤40,1≤M≤), indicating the number of matches and Bobek's worthless property.

In the second line, N# positive integers separated by spaces, none of which exceed , representing the price of tickets for each game.

OUTPUT: OUTPUT a line indicating the number of schemes. Because N is very large, note: the answer is ≤

 ```5 1000 100 1500 500 500 1000``` 8

The eight schemes are:

• I didn't watch a game. I slipped away
• The price is 100 yuan
• The first game of 500 yuan
• The second game with a price of ， 500 ，
• The game with a price of ， 100 ， and the first game with a price of ， 500 ，
• The game with a price of # 100 and the second game with a price of # 500
• Two games with a price of 500 yuan
• Competition with a price of ， 1000 ，
```#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll ans = 0,cnt=0;  //ans records the total number of feasible schemes, and cnt records all feasible schemes in the first half of the interval
ll n, m;
ll f[1 << 21]; //f [] record the cost of all feasible schemes in the first half of the interval
ll mon[45];  //mon [] record the cost of each game

bool cmp(int a, int b) {  //Sorting function
return a > b;
}
void dfs1(ll b,ll cos) {  //First search the depth of the first half interval, b record the starting position, and cos record the cost
if (cos > m)return;  //Cost exceeded, search ended
if (b >= n / 2) {  //When reaching the end of the first half interval and the cost is reasonable, record the cost and add 1 to cnt
f[cnt] = cos;
cnt++;
return;
}
dfs1(b + 1, cos);  //For the ball game with subscript b+1, discuss the two situations of watching and not watching.
dfs1(b + 1, cos + mon[b]);
}
void dfs2(ll b, ll cos) {  //Search the depth of the second half first, b record the starting position, and cos record the cost
if (cos > m)return;
if (b >= n) {  //For each feasible COS in the second half interval, ans is the number of schemes whose cost is less than or equal to (m-cos) in the first half interval
ans += upper_bound(f, f + cnt, m - cos) - f; //The sum of, that is, the sum of the expenses less than the total amount m
return;                                      //Total number of cases
}
dfs2(b + 1, cos);
dfs2(b + 1, cos + mon[b]);
}

int main(){
cin >> n >> m;
for (int i = 0; i < n; i++)cin >> mon[i];  //Enter the amount of each game
sort(mon, mon + n, cmp);     //Sort the amount in descending order, prune and reduce the number of searches
dfs1(0, 0);
sort(f, f + cnt);  //Sort all feasible schemes in ascending order to facilitate subsequent operations
dfs2(n/2, 0);
cout << ans << endl;
}```

# Algorithm Tips:

1. Two way breadth first search:

For the case where the start state and the end state are given and the forward and inverse search can be realized, taking the start state and the end state as the starting point for BFs at the same time can reduce a considerable part of the search amount compared with one-way BFS, and has a good optimization effect compared with the case where there are more expansion nodes and the target node is deep.

Implementation: we add the starting point and the ending point into the queue for BFS at the same time. We mark the points expanded from the starting point and the ending point with different marks. When a point is about to be marked with the second mark, it indicates that the two-way search meets, and the sum of the steps can be output.

2. Pruning:

(1). Search order optimization: different search orders will produce search trees of different sizes. When combining each equal length stick, give priority to the long one to start the search, which can reduce the amount of search status.

(2). Number theory optimization: the length of the combined equal length wooden stick must be the divisor of the total length of all wooden sticks, so we can enumerate the divisor search and test.

(3). Feasibility pruning: if the first one cannot be used, it must be a failure. The last one with equal length does not need to be searched, because it must be the total length.

(4). Eliminate equivalent redundancy: record the place found last time. Don't start searching again from the first one. Ensure that the composition of each equal length wooden stick is searched from long to short. Avoid repeatedly searching for sticks of the same length.

(5)......

3. Iterative search:

The biggest defect of DFS is that each search branch is too deep. When the answer is in a shallow position, it spends too much time in invalid search branches. Iterative deepening search first searches k layers in depth first. If no feasible solution is found, then search k+1 layers in depth first until a feasible solution is found. Since the depth increases gradually from small to large, it can ensure that the search depth is the smallest when the search results are found. The depth of DFS is essentially increased by means of enumeration

4. Half search:

Half search is applicable to the case where the input data is small, but not small enough to use violent search directly. The main idea is to divide the whole search process into two parts, search separately, and finally merge the results of the two parts. The complexity of violent search is often exponential. After using the halved search algorithm, the complexity index can be halved, that is, the complexity can be reduced from O()Down to O ( ).

Topics: Algorithm