传纸条和方格取数(DP)

传纸条

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

①四维DP

首先发现这可能是两种路径(一种是从小渊内边,另一种是从小轩内边),但是我们可以发现很难实现点的清零或者是路径的保存,而且还面对着当前的路径是否为最优解的问题(一开始博主用的贪心)然后博主就用了取模存点的鬼畜算法,真的难搞…… 。
因为贪心问题多多,漏洞百出,状态过多且不好保存,所以,我们考虑DP:因为这是两个人,所以每种状态中只会有两种路径,所以我们可以增加DP的状态从而保存路径。

我们考虑用f[i,j,k,l]f[i,j,k,l]f[i,j,k,l]表示当前一个人到达[i,j][i,j][i,j],另外一个人到达了[k,l][k,l][k,l]的最大好心值。
那么,状态转移方程,也就可以根据二人的走法(向右,向下)进行转移。

code

#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-1(也就是你的步数)(减一是因为我的初始位置在(1,1)(1,1)(1,1)的位置上)
为什么,博主要说如下的话呢??(在这里博主要强烈建议一下,好好想一想)在这里插入图片描述
如果按照博主的思路的话,(两条交叉的路径变成两条起点相同,终点相同,却有不一样的路径。)
这样的话,模拟一下吧(A表示第一条路径,B表示第二条路径)(在这里我们规定A所代表的路径是一直向下走的,B所代表的路径是一直向右走)当然你不信的话,你也可以模拟一个其他的什么
在这里插入图片描述
根据上面所列出的表格可以发现:(XA+XB==sum-1)
因为你只能向下或者是向右,而每一步你走之后,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,今天的这篇就完了。愿你不忘初心,归来仍是少年。︿( ̄︶ ̄)︿

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值