传纸条和方格取数(DP)

传纸条

在这里插入图片描述
在这里插入图片描述
输入输出样例
输入样例#1:
3 3
0 3 9
2 8 5
5 7 0
输出样例#1:
34

①四维DP

首先发现这可能是两种路径(一种是从小渊内边,另一种是从小轩内边),但是我们可以发现很难实现点的清零或者是保存,而且也不清楚自己的是不是最优解(一开始博主用的贪心)然后博主就用了取模存点的鬼畜算法 因为这是两个人,所以会有两种状态,所以用四维DP可以很好的解决这个问题。
下见程序

#include<bits/stdc++.h>
using namespace std;
int m,n;
int a[52][52];
int f[52][52][52][52];

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}

void in()
{
    m=read();
    n=read();
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            a[i][j]=read();
}

int main()
{
    in();
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=m;k++)
                for(int l=1;l<=n;l++)
                {
                    f[i][j][k][l]=max(f[i-1][j][k-1][l]/*同时向下*/,max(f[i-1][j][k][l-1]/*第一个向下,第二个向右*/,max(f[i][j-1][k-1][l]/*第一个向右,第二个向下*/,f[i][j-1][k][l-1]/*同时向右*/)))+a[i][j]+a[k][l];
                    if(i==k&&j==l)	f[i][j][k][l]-=a[i][j];
                }
    printf("%d\n",f[m][n][m][n]);
    return 0;
}

②三维DP

其实按照上面的思路我们还可以搞一下优化,当你模拟的时候,你会惊奇地发现,横坐标和纵坐标的和是一样的,而且正好==step(也就是你的步数)
在这里插入图片描述
大概呢就是,博主上面的图片(emmmmmmmmmmmmmm,看不懂没关系,主要是配个图
为什么,博主要说如下的话呢??(在这里博主要强烈建议一下,好好想一想)在这里插入图片描述
首先,如果按照博主的思路的话,(两条交叉的路径变成两条起点相同,终点相同,却有不一样的路径。)
这样的话,模拟一下吧(A表示第一条路径,B表示第二条路径)
在这里插入图片描述
(在这里我们规定A统统向下走,B统统向右走)当然你不信的话,你也可以模拟一个其他的什么 横纵坐标之和什么的。(XA+XB==sum)
因为你只能向下或者是向右,而每一步你走之后,sum的之就会增加一。(谜团解开,当然,上面都是一些废话什么的
之后,你的DP数组就可以换一下,因为之前是用四维数组表示四种状态,但是现在可以用和sum来表示XA和XB,不就少了一种状态!!!!!!<( ̄ˇ ̄)/
代码如下

#include<bits/stdc++.h>
using namespace std;
#define maxn 52 
int m,n;
int a[maxn][maxn];
int f[2*maxn]/*这里这里的2*maxn十分重要*/[maxn][maxn];

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}

void in()//输入 
{
    m=read();n=read();
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            a[i][j]=read();
}
int main()
{
    in();
    for(int i=1;i<=n+m-1;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)//或许说,此时的i就是你的步数(step) 
            {
                if(i-j+1<1||i-k+1<1)		continue;//灵魂剪枝,判断不存在的点和数组越界问题 
                f[i][j][k]=max(f[i-1][j][k],max(f[i-1][j-1][k],max(f[i-1][j][k-1],f[i-1][j-1][k-1])))+a[i-j+1][j]+a[i-k+1][k];
                if(j==k)	f[i][j][k]-=a[i-j+1][j];
             } 
    printf("%d\n",f[n+m-1][n][n]);
    return 0;
} 

以上就是传纸条的博主的思考

下面是方格取数

其实思路都和上面是一样的(由于博主想让你们自己思考思考 就是博主懒
直接见代码

#include<bits/stdc++.h>
using namespace std;
int n;
int ans=0;
int a[11][11];
int f[11][11][11][11];

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}

void in()
{
    n=read();
    for(;;)
    {
        int t1,t2,t3;
        t1=read();t2=read();t3=read(); 
        if(t1==0&&t2==0&&t3==0)	break;
        else   					a[t1][t2]=t3;
    }
}

int main()
{
    in();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                for(int l=1;l<=n;l++)
                {
                    f[i][j][k][l]=max(f[i-1][j][k-1][l],max(f[i-1][j][k][l-1],max(f[i][j-1][k-1][l],f[i][j-1][k][l-1])))+a[i][j]+a[k][l];
                    if(i==k&&j==l)	f[i][j][k][l]-=a[i][j];
                }
    printf("%d\n",f[n][n][n][n]);//这里是边界以及和上面的区别
    return 0;
}

啦啦啦,最后,还记得博主说的鬼畜的取模存点的鬼畜算法吗,其实内个鬼畜算法是方格取数中的,主要是因为博主不会记录状态,于是乎,将每次取得X,Y记录到另两个数(P,Q)里。
如:
1 x=3,y=4 p=3,q=4
2 x=2,y=1 p=32,q=41
3 x=9 y=6 p=329,q=413
……
而且由于方格取数中都是1~9的数字,所以他们都是一一对应的,取出来也比较方便(这可是博主自己想出来的哦,小骄傲(^-^)V)
代码如下(博主用的贪心水到了80分,也可能是因为数据太弱)

#include<bits/stdc++.h>
using namespace std;
int n,x,y,k,tot;
int ans=0;
int sum=0;
int m1,m2;
int mm1,mm2;
int a[10][10];

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;	
}

void in()
{
    n=read();
    for(int i=1;;i++)
    {
        x=read();y=read();k=read();
        if(x==0&&y==0&&k==0)	break;
        else			a[x][y]=k;	
    }
}

void dfs(int x,int y,int s,int p,int q)
{
    if(x>n||y>n)	return ;
    if(a[x][y]!=0)
    {
        s+=a[x][y];
        p=p*10+x;
        q=q*10+y;
    }
    dfs(x,y+1,s,p,q);
    dfs(x+1,y,s,p,q);
 	if(s>ans)
    {
        ans=s;
        m1=p;m2=q;
    }
}

int main()
{
    in(); 
    sum=a[1][1]+a[n][n];
    a[1][1]=0;a[n][n]=0;
    for(int i=1;i<=n;i++)
    {
        ans=0;
        dfs(1,i,0,0,0);//从第一行开始搜索 
        if(ans>tot)
        {
            tot=ans;
            mm1=m1,mm2=m2;
        }
    }
    sum+=tot;tot=0;
    while(mm1>0)
    {
        int b1=mm1%10;
        int b2=mm2%10;
        a[b1][b2]=0;
        mm1/=10;
        mm2/=10;
    }//这里这里,鬼畜的求模取数
    for(int i=1;i<=n;i++)
    {
        ans=0;
        dfs(1,i,0,0,0);//从第一行开始搜索 
        if(ans>tot)
        {
            tot=ans;
            mm1=m1,mm2=m2;
        }
    }
    sum+=tot;
    cout<<sum<<endl;
    return 0; 
}

ok,今天的这篇就完了,愿你不忘初心,归来仍是少年。︿( ̄︶ ̄)︿

猜你喜欢

转载自blog.csdn.net/qq_43093454/article/details/85254933