传纸条
输入输出样例
输入样例#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,今天的这篇就完了。愿你不忘初心,归来仍是少年。︿( ̄︶ ̄)︿