# 算法实验报告3——分支限界

## 1.艰难旅行问题

• 兔兔
• 兔兔

### DFS

#include<bits/stdc++.h>

const int maxn=2333;
const int maxm=2333;
int mp[maxn][maxm];
int vis[maxn][maxm];
int N,M;

const int dx[] = {0,1,-1,0,0};
const int dy[] = {0,0,0,1,-1};
int dfs(int x,int y){
//  printf("de:%d %d\n",x,y);
int now = mp[x][y];
vis[x][y]=1;
int res = 0;
res++;
for(int i =1;i<=4;++i){
int nx = x+dx[i];
int ny = y+dy[i];
if((nx>N)||(ny>M)||(nx<=0)||(ny<=0))continue;
if((mp[nx][ny]^now )&& (!vis[nx][ny])){
vis[nx][ny]=1;
res+=dfs(nx,ny);
}
}
return res;
}

int main(){
using namespace std;
cin>>N>>M;
for(int i=1;i<=N;++i){
for(int j=1;j<=M;++j){
cin>>mp[i][j];
}
}
int st,sy;
cin>>st>>sy;
int ans=dfs(st,sy);
cout<<ans<<"\n";
}


T1_1.in

5 5
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
0 1 0 1 0
1 0 1 0 1
1 1


T1_2.in

5 5
1 1 0 1 0
1 1 0 1 0
0 0 0 1 0
1 1 1 1 0
0 0 0 0 0
1 1


T1_3.in

4 4
1 0 1 1
1 0 1 0
0 1 0 1
0 0 1 1
3 3


### BFS

#include<bits/stdc++.h>

const int maxn=2333;
const int maxm=2333;
int mp[maxn][maxm];
int vis[maxn][maxm];
int N,M;

const int dx[] = {0,1,-1,0,0};
const int dy[] = {0,0,0,1,-1};

int dfs(int x,int y){
//  printf("de:%d %d\n",x,y);
int now = mp[x][y];
vis[x][y]=1;
int res = 0;
res++;
for(int i =1;i<=4;++i){
int nx = x+dx[i];
int ny = y+dy[i];
if((nx>N)||(ny>M)||(nx<=0)||(ny<=0))continue;
if((mp[nx][ny]^now )&& (!vis[nx][ny])){
vis[nx][ny]=1;
res+=dfs(nx,ny);
}
}
return res;
}

int bfs(int x, int y){
using namespace std;
queue< pair<int,int> > q;
q.push(make_pair(x,y));
int ans = 1;//开始的st sy一定能到达
vis[x][y] = 1;
while(!q.empty()){
pair<int,int> tmp;
tmp = q.front();
q.pop();
int nowx = tmp.first;
int nowy = tmp.second;
int now = mp[nowx][nowy];
for(int i=1;i<=4;++i){
int nx = nowx+dx[i];
int ny = nowy+dy[i];
// printf("deee:%d %d\n",nx,ny);
if((nx>N)||(ny>M)||(nx<=0)||(ny<=0))continue;
if((mp[nx][ny]^now )&& (!vis[nx][ny])){
vis[nx][ny]=1;
q.push(make_pair(nx,ny));
ans++;
}
}
}
return ans;
}

int main(){
using namespace std;
cin>>N>>M;
for(int i=1;i<=N;++i){
for(int j=1;j<=M;++j){
cin>>mp[i][j];
}
}
int st,sy;
cin>>st>>sy;
int ans=bfs(st,sy);
cout<<ans<<"\n";
}


python版本：

from collections import deque

maxn = 2333
maxm = 2333
mp = [[0] * maxm for _ in range(maxn)]
vis = [[0] * maxm for _ in range(maxn)]
N, M = 0, 0

dx = [0, 1, -1, 0, 0]
dy = [0, 0, 0, 1, -1]

def dfs(x, y):
now = mp[x][y]
vis[x][y] = 1
res = 1
for i in range(1, 5):
nx = x + dx[i]
ny = y + dy[i]
if nx > N or ny > M or nx <= 0 or ny <= 0:
continue
if mp[nx][ny] != now and not vis[nx][ny]:
res += dfs(nx, ny)
return res

def bfs(x, y):
q = deque()
q.append((x, y))
ans = 1
vis[x][y] = 1
while q:
nowx, nowy = q.popleft()
now = mp[nowx][nowy]
for i in range(1, 5):
nx = nowx + dx[i]
ny = nowy + dy[i]
if nx > N or ny > M or nx <= 0 or ny <= 0:
continue
if mp[nx][ny] != now and not vis[nx][ny]:
vis[nx][ny] = 1
q.append((nx, ny))
ans += 1
return ans

def main():
global N, M
N, M = map(int, input().split())
for i in range(1, N + 1):
mp[i][1:M+1] = list(map(int, input().split()))

st, sy = map(int, input().split())
ans = bfs(st, sy)
print(ans)

if __name__ == "__main__":
main()



## 2.混乱地铁问题

• 每次换乘作为一层深度，需要求出最小的深度，如果在当前层（当前换层次数下）可以有解，那么后面一层的解一定不如当前的好。

• 根据这个性质BFS会比DFS更先搜索到解，而在这个问题下，我们不需要遍历一整棵搜索树，因此，当搜索到任意一个解的时候，任务完成。

• 可以用邻接表来进行加边。对于$$i$$节点可以到达的节点为$$i-w$$$$i+w$$

int ww ;cin>>ww;


C++(c with STL)代码：

#include<bits/stdc++.h>

const int maxn=114514;
const int maxm=12345;
struct edge{
int u,v,w,nxt;
}edge[maxm<<1];
int fst[maxn];
int cnt;
inline void addedge(int u,int v,int w){
edge[++cnt].u=u,edge[cnt].v=v,edge[cnt].w=w;
edge[cnt].nxt=fst[u];
fst[u]=cnt;
}
int N,M;

int vis[maxn],dis[maxn];
int bfs(int u,int dst){
using namespace std;
queue<int> q;
q.push(u);
vis[u]=1;//当有最优解的时候一个节点最多走一次
dis[u]=0;//进站是0
while(!q.empty()){
int now = q.front();//当前所在的地铁now
q.pop();
if(now==dst)return dis[now];//如果当前到达了目的地就可以退出了。
for(int i = fst[now];i;i=edge[i].nxt){//遍历当前地铁可以去的全部节点
int v = edge[i].v;//即将要去的节点
if(!vis[v]){//如果没有访问到过
// printf("debug___%d->%d\n",now,v);
q.push(v);
vis[v]=1;
dis[v]=dis[now]+1;//更新距离
if(v==dst)return dis[v];//没什么用的优化，提前退出返回答案
}
}
}
return -1;//没有搜索到结果
}

int main(){
using namespace std;
cin>>N>>M;
for(int i=1;i<=N;++i){
int ww ;cin>>ww;
}
int st,dt;cin>>st>>dt;
cout<<bfs(st,dt);
}


python版本：

from collections import deque
maxn = 114514
maxm = 12345
class Edge:
def __init__(self, u=0, v=0, w=0, nxt=0):
self.u = u
self.v = v
self.w = w
self.nxt = nxt
edges = [Edge() for _ in range(maxm << 1)]
fst = [0] * maxn
cnt = 0
global cnt, edges, fst
cnt += 1
edges[cnt].u = u
edges[cnt].v = v
edges[cnt].w = w
edges[cnt].nxt = fst[u]
fst[u] = cnt
N, M = 0, 0
vis = [0] * maxn
dis = [0] * maxn
def bfs(u, dst):
q = deque()
q.append(u)
vis[u] = 1
dis[u] = 0
while q:
now = q.popleft()
if now == dst:
return dis[now]
i = fst[now]
while i:
v = edges[i].v
if not vis[v]:
q.append(v)
vis[v] = 1
dis[v] = dis[now] + 1
if v == dst:
return dis[v]
i = edges[i].nxt
return -1
def main():
global N, M
N, M = map(int, input().split())
for i in range(1, N + 1):
ww = int(input())
if i - ww > 0:
addedge(i, i - ww, 1)  # The weight has no meaning in this problem
if i + ww <= N:
st, dt = map(int, input().split())
print(bfs(st, dt))

if __name__ == "__main__":
main()


## 3.0-1背包问题

• 从dp[N][M]开始，逆向查找哪些物品被放入了背包
• 比较dp[i][j]与dp[i-1][j]是否相等：
• 如果相等，说明第i件物品没有被放入背包。
• 如果不相等，说明第i件物品被放入了背包，将这个物品记录下来，并将j更新为j-w[i]。
• 将i减1，继续比较。
• 重复这个过程，直到i为0或者j为0。

//反求选了哪几个
int i = N;
int j = W;
for(;i>0&&j>0;i--){
if(dp[i][j] != dp[i-1][j]){
res[++cnt]=i;
j = j-w[i];
}
}
for(int i=1;i<=cnt;++i){
printf("sel: %d\n",res[i]);
}


#include<iostream>
const int maxn = 1e4+102;
int dp[maxn][maxn];
int w[maxn],v[maxn];
int N,W;

int res[maxn];
int cnt;

int main(){
using namespace std;
cin>>N>>W;
for(int i=1;i<=N;++i){
cin>>w[i]>>v[i];
}
//dp 不装东西的时候假设价值是0
for(int i=1;i<=N;++i){
dp[i][0]=0;
}
for(int i=1;i<=N;++i){
for(int j=0;j<=W;++j){
if(j-w[i]<0)dp[i][j]=dp[i-1][j];
else dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]]+v[i]);
}
}
cout<<dp[N][W]<<"\n";

//反求选了哪几个
int i = N;
int j = W;
for(;i>0&&j>0;i--){
if(dp[i][j] != dp[i-1][j]){
res[++cnt]=i;
j = j-w[i];
}
}
for(int i=1;i<=cnt;++i){
printf("sel: %d\n",res[i]);
}
}


def main():
N, W = map(int, input().split())
w = [0] * (N + 1)
v = [0] * (N + 1)
for i in range(1, N + 1):
w[i], v[i] = map(int, input().split())

dp = [[0] * (W + 1) for _ in range(N + 1)]

for i in range(1, N + 1):
for j in range(W + 1):
if j - w[i] < 0:
dp[i][j] = dp[i - 1][j]
else:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i])
print(dp[N][W])

res = []
i, j = N, W
while i > 0 and j > 0:
if dp[i][j] != dp[i - 1][j]:
res.append(i)
j -= w[i]
i -= 1
for i in range(len(res)):
print(f"sel: {res[i]}")

if __name__ == "__main__":
main()



## 4.P120第2题

https://www.luogu.com.cn/problem/P1443

• 因为求的是最少走多少步，我们在当前的状态里可以求得答案的话，那么下层状态求的一定是更多的数量。
• 这个性质直接用BFS就可以啦
• 大兔子

N , M ,sx, sy = map(int , input().split())
mp = [[0 for j in range(1,M+1) ]for i in range(1,N+1)]
from collections import deque
# 我的马可以走这些地方
dx = [0,-1,-2,-2,-1,1,2,2,1]
dy = [0,2,1,-1,-2,2,1,-1,-2]
def bfs(stx,sty,dstx,dsty):
# 一个点可以走三次
vis = [[0 for j in range(M+1)]for i in range(N+1)]
dis = [[0 for j in range(M+1)]for i in range(N+1)]
q = deque()
q.append((stx,sty))
dis[stx][sty]=0
vis[stx][sty]=1
while q:
x,y = q.popleft()
if x == dstx and y == dsty:
return dis[x][y]
for i in range(1,9):
# print(i)
nx = x+dx[i]
ny = y+dy[i]
if 1 <= nx <= N and 1 <= ny <= M and vis[nx][ny] == 0:
vis[nx][ny]=1
q.append((nx,ny))
dis[nx][ny]= dis[x][y]+1
return -1

for i in range(N):
for j in range(M):
print(bfs(sx,sy,i+1,j+1),end=" ")
print()


### 虽然没过，试一试c++：

#include<bits/stdc++.h>

const int maxn = 2333;
const int maxm = 2333;
int dx[] = {0,-1,-2,-2,-1,1,2,2,1};
int dy[] = {0,2,1,-1,-2,2,1,-1,-2};
int N,M;
int vis[maxn][maxm];
int dis[maxn][maxm];
#define rep(i,x,y) for(int i=x;i<=y;++i)

inline void clear(){
rep(i,1,N){
rep(j,1,M){
vis[i][j]=0;
dis[i][j]=0;
}
}
}

inline int bfs(int stx,int sty,int dstx,int dsty){
using namespace std;
queue<pair<int, int>> q;
clear();
q.push({stx, sty});
vis[stx][sty] = 1;
while (!q.empty()) {
int x = q.front().first;
int y = q.front().second;
q.pop();
if(x==dstx&&y==dsty){
return dis[dstx][dsty];
}
for(int i=1;i<=8;++i){
int nx = x+dx[i];
int ny = y+dy[i];
if (nx >= 1 && nx <= N && ny >= 1 && ny <= M && vis[nx][ny] == 0) {
vis[nx][ny] = 1;
q.push({nx, ny});
dis[nx][ny] = dis[x][y] + 1;
}
}
}
return -1;
}
int main(){
int sx,sy;
std::cin>>N>>M>>sx>>sy;
rep(i,1,N){
rep(j,1,M){
std::cout<<bfs(sx,sy,i,j)<<" ";
}
std::cout<<"\n";
}
}


### 最后的代码：

#### C++

#include<bits/stdc++.h>

const int maxn = 800;
const int maxm = 800;
int dx[] = {0,-1,-2,-2,-1,1,2,2,1};
int dy[] = {0,2,1,-1,-2,2,1,-1,-2};
int N,M;
int vis[maxn][maxm];
int dis[maxn][maxm];
#define rep(i,x,y) for(int i=x;i<=y;++i)

inline void clear(){
rep(i,1,N){
rep(j,1,M){
vis[i][j]=0;
dis[i][j]=-1;
}
}
}

inline int bfs(int stx,int sty){
using namespace std;
queue<pair<int, int>> q;
clear();
q.push({stx, sty});
vis[stx][sty] =1;
dis[stx][sty] =0;
while (!q.empty()) {
int x = q.front().first;
int y = q.front().second;
q.pop();
for(int i=1;i<=8;++i){
int nx = x+dx[i];
int ny = y+dy[i];
if (nx >= 1 && nx <= N && ny >= 1 && ny <= M && vis[nx][ny] == 0) {
vis[nx][ny] = 1;
q.push({nx, ny});
dis[nx][ny] = dis[x][y] + 1;
}
}
}
return -1;
}
int main(){
int sx,sy;
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cin>>N>>M>>sx>>sy;
bfs(sx,sy);
rep(i,1,N){
rep(j,1,M){
std::cout<<dis[i][j]<<" ";
}
std::cout<<"\n";
}
}


python:

N , M ,sx, sy = map(int , input().split())
mp = [[0 for j in range(1,M+1) ]for i in range(1,N+1)]
from collections import deque
# 我的马可以走这些地方
dx = [0,-1,-2,-2,-1,1,2,2,1]
dy = [0,2,1,-1,-2,2,1,-1,-2]
dis = [[-1 for j in range(M+1)]for i in range(N+1)]
def bfs(stx,sty):
# 一个点可以走三次
global dis
vis = [[0 for j in range(M+1)]for i in range(N+1)]
q = deque()
q.append((stx,sty))
dis[stx][sty]=0
vis[stx][sty]=1
while q:
x,y = q.popleft()
# if x == dstx and y == dsty:
#   return dis[x][y]
for i in range(1,9):
# print(i)
nx = x+dx[i]
ny = y+dy[i]
if 1 <= nx <= N and 1 <= ny <= M and vis[nx][ny] == 0:
vis[nx][ny]=1
q.append((nx,ny))
dis[nx][ny]= dis[x][y]+1
return -1

bfs(sx,sy)
for i in range(N):
for j in range(M):
print(dis[i+1][j+1],end=" ")
print()