分治法(1)旋转矩阵,拿金币,棋盘覆盖

分治法:

参考:

(36条消息) 分治算法详细讲解(含经典例题分析)_nan_black的博客-CSDN博客_分治算法几个经典例子

(36条消息) 经典算法(1)分治法_胡乱huluan的博客-CSDN博客

(1);把a问题分成若干个小问题

(2):保证每一个小问题的解法都相同,并保证这些小问题能整合为a问题的解

(3):用递归或者循环来解每一个小问题

(4):把所有小问题的解整合为a问题的解

1.旋转矩阵:

我们可以通过电脑构造一个螺旋方阵
例如,一个边长为5的螺旋方阵样子如下
1 16 15 14 13
2  17     24  23     12
3  18     25  22     11
4  19     20  21     10
5   6      7    8       9

思路:

把这个方阵分成多层,每层的阶数为(s4个部分(蓝,红,绿,灰)

每个部分都是s-1个数,求解每一部分只需要改变行和列

然后用相同的方法求里层(s-2

最后把所有的分解整合为总问题的解

复制代码
#include<bits/stdc++.h>
using namespace std;
int a[1000][1000];
int size;
void fz()
{
    int s=size;
    int i=0,j=0,num=1;
    int b;
    for(;s!=0;s-=2)//结束标志为方阵的阶数为1或零
    {
        if(s==1)//阶数为1时,直接把num放进方阵中并跳出
        {
            a[i][j]=num;
            break;
        }
        for(b=0;b<s-1;b++,i++)//用一个变量b来控制数量

        {
            a[i][j]=num;
            num++;
         } 
         for(b=0;b<s-1;b++,j++)
         {
             a[i][j]=num;
             num++;
         }
         for(b=0;b<s-1;b++,i--)
         {
             a[i][j]=num;
             num++;
         }
         for(b=0;b<s-1;b++,j--)
         {
             a[i][j]=num;
             num++;
         }
         i++;
         j++;
    }
    for(int i=0;i<=size-1;i++)
    {
        for(int j=0;j<=size-1;j++)
    {
        printf("%5d",a[i][j]);
    }
    cout<<endl; 
    }
    
}
int main()
{
    cin>>size;
    fz();
    return 0;
}
复制代码

 

2.典型二分法:

问题:金块问题

老板有一袋金块(共n块),最优秀的雇员得到其中最重的一块,最差的雇员得到其中最轻的一块,假设有一台比较重量的仪器,我们希望用最少的比较次数找出最重的金块

方法:二分递归

思路

  1. 三种情况:数量为1,数量为2,数量大于2
  2. 第二种情况直接用max找两数最大即可
  3. 第三种情况使用二分法mid=(c+d)/2 ,la=fmax(a,b,mid),ra=fmax(a,mid,d)
  4. 分别找数据前二分之一和后二分之一的值(通过递归求值,最后每一组的最小分化一定是两数比较,即:d-c=1
  5. 从而变为第二种方法求解最大值)最后返回la和ra的最大者。

    Max(n)=Max(1+n/2)+Max(n/2,n)

    复制代码
    #include<bits/stdc++.h>
    using namespace std;
    int a[1000];
    int m;
    int fmax(int a[],int b,int c)
    {
        if(b==c)
        {
            return a[b];
        }
        if(c-b==1)
        {
            int la=a[b];
            int ra=a[c];
            return (max(la,ra));
        }
        if(c-b>=2)
        {
            int mid=(b+c)/2;
            int la=fmax(a,b,mid);
            int ra=fmax(a,mid,c);
            return (max(la,ra));
        }
    }
    int fmin(int a[],int b,int c)
    {
        if(b==c)
        {
            return a[b];
        }
        if(c-b==1)
        {
            int la=a[b];
            int ra=a[c];
            return (min(la,ra));
        }
        if(c-b>=2)
        {
            int mid=(b+c)/2;
            int la=fmin(a,b,mid);
            int ra=fmin(a,mid,c);
            return (min(la,ra));
        }
    }
    int main()
    {
        cout<<"请输入金块数量:";
        cin>>m;
        cout<<"请输入每块金块的重量:"; 
        for(int i=0;i<m;i++)
        {
            cin>>a[i];
        }
        int maxn=fmax(a,0,m-1);
        int minn=fmin(a,0,m-1);
        cout<<"最重的金块:"<<maxn<<endl;
        cout<<"最轻的金块:"<<minn<<endl;
    }
    复制代码

     

    3.二分法不相似情况

    需要构造与原问题相似子问题。

    问题:残缺棋盘

    题目:(37条消息) 分治算法-残缺棋盘_gzj_1101的博客-CSDN博客_残缺棋盘

    思路:

    1. 把棋盘二分为更小的棋盘,每次都切成四部分
    2. 由于原棋盘中只有一个特殊方格,这样划分后,这四个较小的棋盘中只有一个子棋盘包含特殊方格,其余三个棋盘中没有特殊方格。为了将这三个没有特殊方格的子棋盘转化为特殊棋盘,以便采用递归方法求解,可以用一个L型骨牌覆盖这三个较小棋盘的汇合处,从而转化为四个较小规模的棋盘覆盖问题。递归地使用这种划分策略,直到将棋盘分割为1*1的子棋盘

    ——《算法设计与分析——以acm大学程序竞赛在线题库为例》(清华大学出版社(北京))p68-69

    复制代码
    //(tr,tc)为棋盘中右上角的方格坐标
    //(dr,dc)为特殊方格的坐标
    //size为行/列数
    #include<bits/stdc++.h>
    using namespace std;
    int a[1024][1024];
    int tr,dr,tc,dc,size=1,k,title=1; 
    void fz(int tr,int tc,int dr,int dc,int size)
    {
        if(size==1)//结束标志:棋盘已经被划分为1*1
        return;
        int s=size/2; //再次划分棋盘
        int t=title++;//L型骨牌序号
        //左上角 
        if(dr<tr+s&&dc<tc+s)
        {
            fz(tr,tc,dr,dc,s);
        }
    //若不在左上角则用t号L骨牌覆盖右下角(与其他子棋盘的交汇处)
        else    
    {
            a[tr+s-1][tc+s-1]=t;
     //覆盖其余方格
            fz(tr,tc,tr+s-1,tc+s-1,s);
        }
        //右上角
        if(dr<tr+s&&dc>=tc+s)
        {
            fz(tr,tc+s,dr,dc,s);
        }
        else
        {
            a[tr+s-1][tc+s]=t;
            fz(tr,tc+s,tr+s-1,tc+s,s);
        }
        //左下角 
        if(dr>=tr+s&&dc<tc+s)
        {
            fz(tr+s,tc,dr,dc,s);
        }
        else
        {
            a[tr+s][tc+s-1]=t;
            fz(tr+s,tc,tr+s,tc+s-1,s);
        }
        //右下角 
        if(dr>=tr+s&&dc>=tc+s)
        {
            fz(tr+s,tc+s,dr,dc,s);
        }
        else
        {
            a[tr+s][tc+s]=t;
            fz(tr+s,tc+s,tr+s,tc+s,s);
        }
     }
    int main()
    {   cin>>k;
        cin>>dr>>dc;
        for(int i=1;i<=k;i++)
        {
            size=size*2;//根据棋盘的阶数求出棋盘的行和列
        }
        fz(0,0,dr,dc,size);
        for(int i=0;i<size;i++)
        {
            for(int j=0;j<size;j++)
            {
                printf("%-3d",a[i][j]);
            }
            printf("\n");
        }
        return 0;
    }
    复制代码

    //其他三个小棋盘的求法和左上方大体相同,只需要改变tr和tc,不做过多注释。

 

 

posted @   格蕾  阅读(370)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示