算法实验复习五 回溯

罗密欧与朱丽叶的迷宫问题

问题描述

罗密欧与朱丽叶的迷宫。罗密欧与朱丽叶身处一个m×n的迷宫中,如图所示。每一个方格表示迷宫中的一个房间。这m×n个房间中有一些房间是封闭的,不允许任何人进入。在迷宫中任何位置均可沿8 个方向进入未封闭的房间。罗密欧位于迷宫的(p,q)方格中,他必须找出一条通向朱丽叶所在的(r,s)方格的路。在抵达朱丽叶之前,他必须走遍所有未封闭的房间各一次,而且要使到达朱丽叶的转弯次数为最少。每改变一次前进方向算作转弯一次。请设计一个算法帮助罗密欧找出这样一条道路。

编程任务:

对于给定的罗密欧与朱丽叶的迷宫,编程计算罗密欧通向朱丽叶的所有最少转弯道路。

数据输入:

输入第一行有3个正整数n,m,k,分别表示迷宫的行数,列数和封闭的房间数。接下来的k行中,每行2个正整数,表示被封闭的房间所在的行号和列号。最后的2 行,每行也有2 个正整数,分别表示罗密欧所处的方格(p,q)和朱丽叶所处的方格(r,s)。

结果输出:

输出计算出的罗密欧通向朱丽叶的最少转弯次数和有多少条不同的最少转弯道路。输出的第1行是最少转弯次数。输出的第2行是不同的最少转弯道路数。接下来的n行每行m个数,表示迷宫的一条最少转弯道路。A[i][j]=k表示第k步到达方格(i,j);A[i][j]=-1 表示方格(i,j)是封闭的。

如果罗密欧无法通向朱丽叶则输出“No Solution!”。

输入示例

3 4 2

1 2

3 4

1 1

2 2

输出示例

6

7

1 -1 9 8

2 10 6 7

3 4 5 -1

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
int road[1000][1000],best[1000][1000];
bool flag=false;
int min_rotation=1000,cur_rotation=0;
int min_count=0;
typedef struct point
{
    int x,y;
}point;
point Start,End;
int dx[8]={1,0,-1,0,1,1,-1,-1};
int dy[8]={0,1,0,-1,1,-1,1,-1};
bool check(point pos)
{
    if(pos.x>0&&pos.x<=n&&pos.y>0&&pos.y<=m&&road[pos.x][pos.y]==0)
        return true;
    return false;
}
void upgrade()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            best[i][j]=road[i][j];
        }
    }
    flag=true;
}
//1.所有房间都遍历过 2.当前位置是朱丽叶 3.转弯次数少于历史转弯次数
void dfs(int depth,point pos,int d)
{
    if(depth==m*n-k&&pos.x==End.x&&pos.y==End.y&&cur_rotation<=min_rotation)
    {
        if(cur_rotation<min_rotation)
        {
            min_rotation=cur_rotation;
            min_count=1;
            upgrade();
        }
        else
        {
            min_count++;
        }
        return;
    }
    else
    {
        point next_pos;
        next_pos.x=pos.x+dx[i];
        next_pos.y=pos.y+dy[i];
        if(check(next_pos))
        {
            road[next_pos.x][next_pos.y]=depth+1;
            if(depth>1&&d!=i) cur_rotation++;
            if(cur_rotation<=min_rotation)
                dfs(depth+1,next_pos,i);
            road[next_pos.x][next_pos.y]=0;
            if(depth>1&&d!=i) cur_rotation--;
        }
    }
}
int main()
{
    cin>>n>>m>>k;
    for(int i=1;i<=k;i++)
    {
        point lock;
        cin>>lock.x>>lock.y;
        road[lock.x][lock.y]=-1;
    }
    cin>>Strat.x>>Start.y>>End.x>>End.y;
    road[Start.x][Start.y]=1;
    dfs(1,Start,0);
    if(flag)
    {
        cout<<min_rotation<<endl;
        cout<<min_count<<endl;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                cout<<best[i][j]<<" ";
            }
            cout<<endl;
        }
    }
    else
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                cout<<road[i][j]<<" ";
            }
            cout<<endl;
        }
        cout<<"No Solution!"<<endl;
    }
    return 0;
}

n色方柱问题

问题描述

设有 n 个立方体,每个立方体的每一面用红、黄、蓝、绿等 n 种颜色之一染色。要把这n 个立方体叠成一个方形柱体,使得柱体的 4 个侧面的每一侧均有 n 种不同的颜色。试设计一个回溯算法,计算出 n 个立方体的一种满足要求的叠置方案。

对于给定的 n 个立方体以及每个立方体各面的颜色,计算出 n 个立方体的一种叠置方案,使得柱体的 4 个侧面的每一侧均有 n 种不同的颜色。

数据输入:

第一行有 1 个正整数 n,0< n< 27,表示给定的立方体个 数和颜色数均为 n。第 2 行是 n 个大写英文字母组成的字符串。该字符串的第 k(0≤ k< n) 个字符代表第 k 种颜色。接下来的 n 行中,每行有 6 个数,表示立方体各面的颜色。立方体各面的编号如下图所示。

 

图中 F 表示前面,B 表示背面,L 表示左面,R 表示右面,T 表示顶面,D 表示底面。相应地,2 表示前面,3 表示背面,0 表示左面,1 表示右面,5 表示顶面,4 表示底面。

结果输出:

输出n个立方体的一种可行的叠置方案。每行 6 个字符,表示立方体个面的颜色。如果不存在所要求的叠置方案,输出 “No Solution”

输入示例:

RGBY 

0 2 1 3 0 0 

3 0 2 1 0 1 

2 1 0 2 1 3 

1 3 3 0 2 2

输出示例:

RBGYRR 

YRBGRG 

BGRBGY 

GYYRBB

#include<bits/stdc++.h>
using namespace std;
const int MAX=150;
int board[MAX][6];  //存储n个立方体各面的颜色
int solu[MAX][6];   //存储解
int n;  //立方体个数、颜色种数
int ans=0;  //解的个数
int used[MAX];
char color[MAX];

//找到一个解后,输出
void out(int edge[])
{
    int i, j, k, a, b, c, d;
    for(i=0; i<2; i++) //2个子图
    {
        for(j=0; j<n; j++)
            used[j] = 0;
        do{
            j = 0;
            d = c = -1;
            while(j<n && used[j]>0) //找下一条未用的边
                j++;
            if(j < n)
                do{
                    a = board[j][edge[i*n+j]*2];
                    b = board[j][edge[i*n+j]*2+1];
                    if(b == d)  //如果上一条边的终点与b相同,说明b为始点,交换,保证a为始点
                        swap(a, b);  //保证有向边的始点对应于前面和左面,终点对应于背面和右面
                    solu[j][i*2] = a;
                    solu[j][i*2+1] = b;
                    used[j] = 1;
                    if(c<0)  //开始顶点
                        c = a;
                    d = b;
                    for(k=0; k<n; k++)  //找下一个立方体
                        if(used[k]==0 && (board[k][edge[i*n+k]*2]==b || board[k][edge[i*n+k]*2+1]==b))
                            j = k;
                }while(b != c);  //找了一圈,回到起点
        }while(j<n);  //所有立方体都找遍
    }
    for(j=0; j<n; j++) //立方体的顶面和底面的颜色
    {
        k = 3 - edge[j] - edge[j+n];
        a = board[j][k*2];
        b = board[j][k*2+1];
        solu[j][4] = a;
        solu[j][5] = b;
    }
    for(i=0; i<n; i++)
    {
        for(j=0; j<6; j++)
            cout << color[solu[i][j]];
        cout << endl;
    }
}

void search()
{
    int i, t, cube;
    bool ok, newg, over;
    int *vert = new int[n];  //记录子图中每个顶点的度,应均为2
    int *edge = new int[n*2];  //记录每个立方体中边被选用的条数,每个立方体只有3条边,有两个子图要选用
    for(i=0; i<n; i++)
        vert[i] = 0;
    t = -1;
    newg = true;
    while(t > -2)
    {
        t++;
        cube = t % n;  //每个立方体找2次,得到真实的立方体编号,也是子图中边的编号
        if(newg)  //如果没有边被选入子图
            edge[t] = -1;
        over = false;  //是否结束,即两个子图构建完成
        ok = false;    //标记边是否已用过,两个子图不应有公共边
        while(!ok && !over)
        {
            edge[t]++;  //边被选用加入子图,使用次数增加
            if(edge[t]>2)  //在立方体每对相对面的顶点连一条边,每个立方体只有3条边
                over = true;
            else
                ok = (t<n || edge[t]!=edge[cube]);  //是否已用过
        }
        if(!over)
        {          //检测边的两个顶点的度
            if(++vert[board[cube][edge[t]*2]] > 2+t/2*2) //如果是第一个子图,顶点度不能超过2
                ok = false;              //如果是第二个子图,加上第一个子图,顶点度不能超过4
            if(++vert[board[cube][edge[t]*2+1]] > 2+t/2*2)
                ok = false;
            if(t%n == n-1 && ok)  //如果一个或两个子图已构建完成
                for(i=0; i<n; i++)
                    if(vert[i] > 2+t/n*2)
                        ok = false;
            if(ok)
            {
                if(t == n*2-1) //找到解
                {
                    ans++;
                    out(edge);
                    return;
                }
                else
                    newg = true;
            }
            else //取下一条边
            {
                --vert[board[cube][edge[t]*2]];  //边的两个顶点
                --vert[board[cube][edge[t]*2+1]];
                t--;
                newg = false;
            }
        }
        else //回溯
        {
            t--;
            if(t > -1)
            {
                cube = t % n;
                --vert[board[cube][edge[t]*2]];
                --vert[board[cube][edge[t]*2]];
            }
            t--;
            newg = false;
        }
    }
}

int main()
{
    cin >> n;
    for(int i=0; i<n; i++)
    {
        cin >> color[i];
    }
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<6; j++)
        {
            cin >> board[i][j];
        }
    }
    search();
    if(ans == 0)
        cout << "No Solution!\n";
    cout << endl;
    return 0;
}

最佳调度问题

问题描述:

假设有 n 个任务由 k 个可并行工作的机器来完成。完成任务 i 需要时间为ti ,设计完成这 n 个任务的最佳调度算法,使得完成全部任务的时间最早。

编程任务:

对任意给定的整数n和k,以及完成任务i需要的时间为ti,i=1,2,…,n。编程计算完成这n个任务的最佳调度。

输入样例:(第一行为任务数n,第二行为可并行工作的机器数k,第三行为机器完成任务i所需的单位时间)

10

7

67 45 80 32 59 95 37 46 28 20

输出样例:(第一行是完成所有任务的最优化时间)

95

#include<bits/stdc++.h>
using namespace std;
int n,k,ans=99999;
int work[1000],mac[1000];
void dfs(int x,int y)
{
    if(ans<=y) return;
    if(x==n+1)
    {
        if(ans>y) ans=y;
        return;
    }
    for(int i=1;i<=k;i++)
    {
        if(mac[i]+work[x]<ans)
        {
            mac[i]+=work[x];
            dfs(x+1,max(y,mac[i]));
            mac[i]-=work[x];
        }
    }
    return;
}
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        cin>>work[i];
    }
    dfs(1,0);
    cout<<ans;
    return 0;
}

运动员最佳匹配问题

问题描述:

羽毛球队有男女运动员各n 人。给定2 个n×n 矩阵P 和Q。P[i][j]是男运动员i 和女运动员j配对组成混合双打的男运动员竞赛优势;Q[i][j]是女运动员i和男运动员j配合的女运动员竞赛优势。由于技术配合和心理状态等各种因素影响,P[i][j]不一定等于Q[j][i]。男运动员i和女运动员j配对组成混合双打的男女双方竞赛优势为P[i][j]*Q[j][i]。设计一个算法,对于给定的男女运动员竞赛优势,计算男女运动员最佳配对法,使各组男女双方竞赛优势的总和达到最大。

编程任务:

给定2 个n×n 矩阵P 和Q。P[i][j]是男运动员i 和女运动员j配对组成混合双打的男运动员竞赛优势;Q[i][j]是女运动员i和男运动员j配合的女运动员竞赛优势。设计一个算法,对于给定的男女运动员竞赛优势,计算男女运动员最佳配对法,使得()的值最大化。并输出最大值。

输入样例:(第一行是男队员(或女队员)的个数,第二、三、四行是男运动员i 和女运动员j配对组成混合双打的男运动员竞赛优势,第五、六、七行是女运动员i和男运动员j配合的女运动员竞赛优势)

3

10 2 3

2 3 4

3 4 5

2 2 2

3 5 3

4 5 1

输出样例:(第一行是竞赛优势的最大和)

52

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000;
int n;
int p[maxn][maxn],q[maxn][maxn],pq[maxn][maxn];
int pre[maxn];
int now=0,ans;
bool vis[maxn]={false};
void dfs(int x)
{
   if(x>n)
   {
      ans=max(ans,now);
      return;
   }
   if(now+pre[n]-pre[x-1]<ans) return;
   for(int j=1;j<=n;j++)
   {
      if(vis[j]) continue;
      vis[j]=true;
      now+=pq[x][j];
      dfs(x+1);
      vis[j]=false;
      now-=pq[x][j];
   }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cin>>p[i][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            cin>>q[i][j];
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            pq[i][j]=p[i][j]*q[j][i];
        }
    }
    //不考虑其他任何因素,前i个组合的最大值
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            pre[i]=max(pre[i],pq[i][j]);
        }
        pre[i]+=pre[i-1];
    }
    dfs(1);
    cout<<ans;
    return 0;
}

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值