PTA 回溯法

 文章目录

一、函数题

1.0/1背包问题 (队列式分枝限界法)

 2.0/1背包问题 (优先队列分枝限界法)

二、编程题

1.子集和问题

2.八皇后问题(*)

3.0-1背包

4.工作分配问题

5.德邦国王

6.图着色问题


一、函数题

1.0/1背包问题 (队列式分枝限界法)

0/1背包问题。给定一载重量为m的背包及n个重量为wi、价值为vi的物体,1≤i≤n,要求把物体装入背包,使背包的物体价值最大。

函数接口定义:

void bound(NodeType &e); //算分枝结点e的上界
void EnQueue(NodeType e,queue &qu);//结点e进队qu
void bfs();//求0/1背包的最优解

void bound(NodeType &e) //算分枝结点e的上界
{
    int i=e.i+1,sumw=e.w;
    double sumv=e.v;
    while((sumw+w[i]<=W)&&i<=n)
    {
        sumw+=w[i],sumv+=v[i];
        i++;
    }
    if(i<=n)e.ub=sumv+(W-sumw)*v[i]/w[i];
    else e.ub=sumv;
}
void EnQueue(NodeType e,queue &qu)//结点e进队qu
{
    if(e.i==n)
    {
        if(e.v>maxv)
        {
            maxv=e.v;
            for(int i=1;i<=n;i++)
                bestx[i]=e.x[i];
        }
    }
    else qu.push(e);
}
void bfs()//求0/1背包的最优解
{
    NodeType e1,e2,e;
    e.i=e.w=e.v=0;e.no=total++;
    queue qu;
    for(int i=1;i<=n;i++)e.x[i]=0;
    bound(e);qu.push(e);
    while(!qu.empty())
    {
        e=qu.front();qu.pop();
        if(e.w+w[e.i+1]<=W)
        {
            e1.i=e.i+1;
            e1.w=e.w+w[e1.i],e1.v=e.v+v[e1.i];
            e1.no=total++;
            for(int i=1;i<=n;i++)e1.x[i]=e.x[i];
            e1.x[e1.i]=1;
            bound(e1);EnQueue(e1,qu);
        }
        e2=e,e2.i++;
        e2.x[e2.i]=0;e2.no=total++;
        bound(e2);
        if(e2.ub>maxv)EnQueue(e2,qu);
    }
}

 2.0/1背包问题 (优先队列分枝限界法)

0/1背包问题。给定一载重量为m的背包及n个重量为wi、价值为vi的物体,1≤i≤n,要求把物体装入背包,使背包的物体价值最大。

函数接口定义:

void bound(NodeType &e);            //计算分枝结点e的上界
void EnQueue(NodeType e,priority_queue &qu);    //结点e进队qu
void bfs();                            //求0/1背包的最优解

void bound(NodeType &e) //算分枝结点e的上界
{
    int i=e.i+1,sumw=e.w;
    double sumv=e.v;
    while((sumw+w[i]<=W)&&i<=n)
    {
        sumw+=w[i],sumv+=v[i];
        i++;
    }
    if(i<=n)e.ub=sumv+(W-sumw)*v[i]/w[i];
    else e.ub=sumv;
}
void EnQueue(NodeType e,priority_queue &qu)    //结点e进队qu
{
    if(e.i==n)
    {
        if(e.v>maxv)
        {
            maxv=e.v;
            for(int i=1;i<=n;i++)
                bestx[i]=e.x[i];
        }
    }
    else qu.push(e);
}
void bfs()//求0/1背包的最优解
{
    NodeType e1,e2,e;
    e.i=e.w=e.v=0;e.no=total++;
    priority_queue qu;
    for(int i=1;i<=n;i++)e.x[i]=0;
    bound(e);qu.push(e);
    while(!qu.empty())
    {
        e=qu.top();qu.pop();
        if(e.w+w[e.i+1]<=W)
        {
            e1.i=e.i+1;
            e1.w=e.w+w[e1.i],e1.v=e.v+v[e1.i];
            e1.no=total++;
            for(int i=1;i<=n;i++)e1.x[i]=e.x[i];
            e1.x[e1.i]=1;
            bound(e1);EnQueue(e1,qu);
        }
        e2=e,e2.i++;
        e2.x[e2.i]=0;e2.no=total++;
        bound(e2);
        if(e2.ub>maxv)EnQueue(e2,qu);
    }
}

二、编程题

1.子集和问题

设集合S={x1,x2,…,xn}是一个正整数集合,c是一个正整数,子集和问题判定是否存在S的一个子集S1,使S1中的元素之和为c。试设计一个解子集和问题的回溯法,并输出利用回溯法在搜索树(按输入顺序建立)中找到的第一个解。

输入格式:

输入数据第1行有2个正整数n和c,n表示S的大小,c是子集和的目标值。接下来的1行中,有n个正整数,表示集合S中的元素。
是子集和的目标值。接下来的1 行中,有n个正整数,表示集合S中的元素。

输出格式:

输出利用回溯法找到的第一个解,以空格分隔,最后一个输出的后面有空格。当问题无解时,输出“No Solution!”。

#include
using namespace std;
const int N=1e5+10;
int n,c,sum,all;
bool st[N];
int a[N];
vector v,res;
void dfs(int u)
{
    if(u==n)
    {cout<<"No Solution!";return;}
    if(sum==c)
    {
        for(auto x:v)res.push_back(x);
        return ;
    }
    for(int i=0;i>n>>c;
    for(int i=0;i>a[i],all+=a[i];
    if(all


2.八皇后问题(*)

在国际象棋中,皇后是最厉害的棋子,可以横走、直走,还可以斜走。棋手马克斯·贝瑟尔 1848 年提出著名的八皇后问题:即在 8 × 8 的棋盘上摆放八个皇后,使其不能互相攻击 —— 即任意两个皇后都不能处于同一行、同一列或同一条斜线上。例如:

PTA 回溯法_第1张图片

现在我们把棋盘扩展到 n×n 的棋盘上摆放 n 个皇后,请问该怎么摆?

请编写程序,输入正整数 n,输出全部摆法(棋盘格子空白处显示句点“.”,皇后处显示字母“Q”,每两个字符之间空一格)。

输入格式:

正整数 n(n>0)

输出格式:

若问题有解,则输出全部摆法(每两种摆法之间空一行)。
若问题无解,则输出 None。

要求:试探的顺序按从上到下逐行进行,其中每一行按从左到右的逐格进行,请参看输出样例2。

#include
using namespace std;
const int N=20;
int n;
char x[N][N];
bool row[N], col[N], dg[N], udg[N];
bool flag=false;
void dfs(int u)
{
    if(u==n)
    {
        if(flag)cout<>n;
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
            x[i][j] = '.';
    dfs(0);
    if(!flag)cout<<"None";
    return 0;
}

3.0-1背包

给定n(n<=100)种物品和一个背包。物品i的重量是wi(wi<=100),价值为vi(vi<=100),背包的容量为C(C<=1000)。
应如何选择装入背包中的物品,使得装入背包中物品的总价值最大? 在选择装入背包的物品时,对每种物品i只有两个选择:装入或不装入。不能将物品i装入多次,也不能只装入部分物品i。

输入格式:

共有n+1行输入:
第一行为n值和c值,表示n件物品和背包容量c;
接下来的n行,每行有两个数据,分别表示第i(1≤i≤n)件物品的重量和价值。

输出格式:

输出装入背包中物品的最大总价值。

#include
using namespace std;
const int N=1e5+10;
int w[N],v[N],f[N];
int main()
{
    int n,c;cin>>n>>c;
    for(int i=1;i<=n;i++)cin>>w[i]>>v[i];
    for(int i=1;i<=n;i++)
    {
        for(int j=c;j>=w[i];j--)
        {
            f[j]=max(f[j],f[j-w[i]]+v[i]);
        }
    }
    cout<


4.工作分配问题

设有n件工作分配给n个人。将工作i分配给第j个人所需的费用为cij 。 设计一个算法,对于给定的工作费用,为每一个人都分配1 件不同的工作,并使总费用达到最小。

输入格式:

输入数据的第一行有1 个正整数n (1≤n≤20)。接下来的n行,每行n个数,表示工作费用。

输出格式:

将计算出的最小总费用输出到屏幕。

#include
using namespace std;
const int N=20;
int a[N][N];
bool st[N];
vector v;
int n,res,ans=0x3f3f3f3f;
void dfs(int u)
{
    if(u==n)
    {ans=min(ans,res);return ;}
    for(int i=0;i>n;
    for(int i=0;i>a[i][j];
    dfs(0);
    cout<

5.德邦国王

 在遥远的德邦草原,有一个古老的国度。
 这里的国王有一种特殊的能力,他可以在限定次数内互换自己和某些子民的位置
 现在国王需要让自己的子民排列成一个整齐的方阵接受检阅,但是他们的动作太慢了
 于是国王决定用自己的能力来完成剩余的排列,从而将自己的子民排列
 成能让他满意的样子。 

 请你帮忙计算国王最少需要瞬间移动多少次,才能将方阵变成他想要的样子。 
 注意国王不得移动出方阵,国王的瞬间移动方式将由输入数据给出。     

输入格式:

N K M分别表示矩阵的大小、国王的瞬移方法数量和国王的瞬间移动限定次数 
接下来 K 行 Xi Yi 表示国王可以让自己和 (X + Xi, Y + Yi)上的子民位置互换
其中 X Y 表示国王当前的位置
接下来 N 行为一个矩阵,表示当前的矩阵排列
接下来 N 行为一个矩阵,表示能让国王满意的矩阵排列 
矩阵仅由(0, 1, 2)组成,其中 0 表示女性子民,1 表示男性子民, 2 表示国王  
1 <= N <= 5 
1 <= K <= 8 
1 <= M <= 15

输出格式:

如果国王可以在限定次数内将矩阵变成他喜欢的样子,请你输出最小次数
反之,请你输出 -1 

#include
using namespace std;
const int N=100;
int a[N],b[N],f1[N][N],f2[N][N];
int n,k,m,X,Y;
int ans=0x3f3f3f3f;
void dfs(int x,int y,int u)
{
    int s=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(f1[i][j]!=f2[i][j])s++;
    if(s==0){ans=min(ans,u);return ;}
    if(u>ans)return ;
    if(s>m-u)return ;
    for(int i=0;i0&&dx<=n&&dy>0&&dy<=n)
        {
            swap(f1[x][y],f1[dx][dy]);
            dfs(dx,dy,u+1);
            swap(f1[x][y],f1[dx][dy]);
        }
    }
}
int main()
{
    cin>>n>>k>>m;
    for(int i=0;i>a[i]>>b[i];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            cin>>f1[i][j];
            if(f1[i][j]==2)
                X=i,Y=j;
        }
    for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)cin>>f2[i][j];
    dfs(X,Y,0);
    if(ans>m)cout<<-1;
    else cout<

6.图着色问题

图着色问题是一个著名的NP完全问题。给定无向图G=(V,E),问可否用K种颜色为V中的每一个顶点分配一种颜色,使得不会有两个相邻顶点具有同一种颜色?

但本题并不是要你解决这个着色问题,而是对给定的一种颜色分配,请你判断这是否是图着色问题的一个解。

输入格式:

输入在第一行给出3个整数V(0

输出格式:

对每种颜色分配方案,如果是图着色问题的一个解则输出Yes,否则输出No,每句占一行。

#include
using namespace std;
const int N=1010;
int m[N][N],c[N];
int main()
{
    int v,e,k;cin>>v>>e>>k;
    while(e--)
    {
        int a,b;cin>>a>>b;
        m[a][b]=m[b][a]=1;
    }
    int n;cin>>n;
    while(n--)
    {
        bool st[N]={0},flag=true;int num=0;
        for(int i=1;i<=v;i++)
        {
            cin>>c[i];
            if(!st[c[i]])num++,st[c[i]]=1;
        }
        if(num!=k){cout<<"No"<

你可能感兴趣的:(算法,c++)