算法实验报告3——分支限界
可访问链接:
1.艰难旅行问题
现已知一个大小为 N · M 的地图,地图中只有可能出现两个数字:0 或 1,规定如果位于数字为 0 的格子上,则下一步只能往相邻四个格子中数字为 1 的格子走,如果位于数字为 1 的格子上,则下一步只能往相邻四个格子中数字为 0 的格子走。如果给定起点格子,则可以向上下左右四个方向移动,且同一个格子不能重复走,求能在地图中到达多少格子?
- 兔兔
- 兔兔
我觉得这个题深搜广搜都可以。
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
样例:
样例1:
样例2:
样例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.混乱地铁问题
在某一个城市中地铁网极度混乱。一条地铁线路上有 n 个地铁站,分别编号为 1 到 n。地铁线路上的每一个站都会停靠地铁,每一个地铁站上都有一个数字 m,代表从此站出发乘客必须乘坐的站数。
每个地铁站都有通往两个方向的地铁。因此既可以向编号大的方向前进 m 站,也可以向编号小的方向前进 m 站。但如果前进后超出了地铁站的范围,则该地铁不可被乘坐。例如编号为 1 的地铁上的数字为 3,那么在该地铁站上车,可以向正方向坐车到 4 号地铁站。但不能反方向坐车到 -2 号地铁站,因为 -2 号地铁站不存在。现在乘客从 A 号地铁站出发,想要到达 B 号地铁站,求他能否到达,最少要搭乘多少次地铁?
-
每次换乘作为一层深度,需要求出最小的深度,如果在当前层(当前换层次数下)可以有解,那么后面一层的解一定不如当前的好。
-
根据这个性质BFS会比DFS更先搜索到解,而在这个问题下,我们不需要遍历一整棵搜索树,因此,当搜索到任意一个解的时候,任务完成。
-
可以用邻接表来进行加边。对于\(i\)节点可以到达的节点为\(i-w\)和\(i+w\)
int ww ;cin>>ww; if(i-ww>0)addedge(i,i-ww,1);//权值在此题没有意义 if(i+ww<=N)addedge(i,i+ww,1);
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;
if(i-ww>0)addedge(i,i-ww,1);//权值在此题没有意义
if(i+ww<=N)addedge(i,i+ww,1);
}
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
def addedge(u, v, w):
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:
addedge(i, i + ww, 1)
st, dt = map(int, input().split())
print(bfs(st, dt))
if __name__ == "__main__":
main()
3.0-1背包问题
在非空间压缩的基础上(二维dp数组),如何反求出我选了什么?
其实挺简单:
- 从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]);
}
完整C语言:
#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]);
}
}
具体的python实现:
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题
已知一个n·m的国际象棋棋盘,现给定一个起始点、终点。若在起始点上有一个马,问:从起始点出发,马能否到达终点,到终点最少需要走几步?输入中包含棋盘的大小、起始点坐标、终点坐标。(马走“日”字。)
这个
比较神奇,它可以走日字:
找了个差不多的题目
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++:
用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";
}
}
想了想,对于这个题,目标其实不变。所以不用bfs多次,bfs一次就行
C++:
python:
错怪python了
最后的代码:
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()