关于递归思考问题的启发

动态规划是一种编程技巧【优化】而不是算法,对于动态规划来说,最基础的就是递归式分解问题。

所以,学会动态规划的第一步,是学会分解问题。

例题:旋转打印矩阵,给你一个矩阵,要求将矩阵内元素旋转打印:

即:  1 2 3 4

         5 6 7 8

         9 10 11 12

打印结果为1 2 3 4 8 12 11 10 9 5 6 7。

方法1:用坐标指针进行顺时针遍历的同时进行打印,需要有的参数是一个bool类型的矩阵表示这个点是否已被访问过或者是否是边界。

方法2:我们注意到,任何矩阵都可以按照矩形框来分解。而我们需要的参量,就是他的左上端点和右下端点,用以防止越界遍历。

于是这题的解法有了,我们需要设计一个printEdge函数,给两个点的坐标,顺时针打印出以这两个点为端点构成的矩形的边框元素。

代码:

#include<cstdio>
using namespace std;
int Array[100][100];

void printEdge(int lx,int ly,int rx,int ry){
    if(lx==rx){//临界打印条件  竖直线 
        for(int j=ly;j<=ry;j++){
            printf("%d ",Array[lx][j]);
        }
    }else if(ly==ry){//水平线  
        for(int j=lx;j<=rx;j++){
            printf("%d ",Array[j][ly]);
        }
    }else{
        int curx=lx;
        int cury=ly;
        while(cury!=ry){
            printf("%d ",Array[curx][cury++]);
        }
        while(curx!=rx){
            printf("%d ",Array[curx++][cury]);
        }
        while(cury!=ly){
            printf("%d ",Array[curx][cury--]);
        }
        while(curx!=lx){
            printf("%d ",Array[curx--][cury]);
        }
    }
}

int main(){
    int n,stl=1,str=1,ovl,ovr;
    scanf("%d",&n);
    ovl=ovr=n;
    for(register int i=1;i<=n;i++){
        for(register int j=1;j<=n;j++){
            scanf("%d",&Array[i][j]);
        }
    } 
    while(stl<=ovl&&str<=ovr){
        printEdge(stl++,str++,ovl--,ovr--);
    }

变:将正方形旋转90°,打印结果,要求O(1)空间复杂度。

思考:如果空间上不约束,可以将元素用上述方法读,然后填入辅助矩阵,或者可以将上述元素读进string,旋转的结果只是在回填的时候给这个string增加了偏移量而已。举例说明,上例矩阵用string存储读取外层矩形框,1 2 3 4 8 12 11 10 9 5 ,旋转90°只是从9开始填这个框而已。

空间上约束:其实上述的string只是一个变量,理论上说已经算O(1)的复杂度了,不过这里给出另一个更好的实现。

1.分解问题:我们还是将正方形按照框分解,把每个外框旋转对了,正方形就转好了。

2.如何不用string旋转框呢?我们以上个例子的矩阵最外框为例,按照旋转后的对应位置将框的元素分组,也就是,1->4->12->9为一组,一共可以分三组【也就是左上和右下坐标的横或纵坐标差】

那么我们把为同一组的元素抽象,A(lx,ly+i)->A(lx+i,ry)->A(rx,ry-i)->A(rx-i,ly),于是核心实现变成了四个变量交换:

#include<cstdio>
using namespace std;
int A[100][100];
void rotateEdge(int lx,int ly,int rx,int ry){
    int len=rx-lx,temp;
    for(int i=0;i<len;i++){
        temp=A[lx][ly+i];
        A[lx][ly+i]=A[rx-i][ly];//4->1
        A[rx-i][ly]=A[rx][ry-i];//3->4
        A[rx][ry-i]=A[lx+i][ry];//2->3
        A[lx+i][ry]=temp;//1->2
    }
}

int main(){
    int n,lx,ly,rx,ry;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&A[i][j]);
        }
    } 
    lx=ly=1;
    rx=ry=n;
    while(lx<rx){
        rotateEdge(lx++,ly++,rx--,ry--);
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            printf("%d ",A[i][j]);
        }
        printf("\n");
    } 
    return 0;
}
 

例题2:“之”字形打印矩阵,打印方式去下图:

切忌被题目迷惑,嘴上说是之形打印,其实就是按照斜线将矩阵分层输出罢了。给定上顶点和下顶点,由于这里的斜线都是斜率为1的,我们可以得到所有的点,只要交替着从上向下到从下到上即可。

解题方法:实现一个函数,给定上下端点,根据一个Bool值选择从上至下还是从下至上。

#include<cstdio>
#include<algorithm>
using namespace std;
int A[100][100];

void printLine(int ux,int uy,int dx,int dy,int flag){
    int sx,sy;
    if(flag){//下到上 
        sx=dx,sy=dy;
        while(sx>=ux){
            printf("%d ",A[sx][sy]);
            sx--;
            sy++;
        }    
    }else{
        sx=ux,sy=uy;
        while(sx<=dx){
            printf("%d ",A[sx][sy]);
            sx++;
            sy--;
        }
    }
}

int main(){
    int m,n,ux,uy,dx,dy;
    bool flag=true;
    scanf("%d%d",&m,&n);
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++){
            scanf("%d",&A[i][j]);
        }
    }
    ux=uy=1;
    dx=dy=1;
    while(ux!=m){
        printLine(ux,uy,dx,dy,flag);
        flag=!flag;
        ux=uy==n?ux+1:ux;//看上端点有没有到右端    
        uy=uy==n?uy:uy+1;
        dy=dx==m?dy+1:dy;//看下端点有没有到底端 
        dx=dx==m?dx:dx+1;
    }
    printf("%d",A[m][n]);
    return 0;

猜你喜欢

转载自blog.csdn.net/qq_39304630/article/details/82115197
今日推荐