2008 noip 传纸条

https://www.luogu.org/problemnew/show/P1006

先放题

Description

题意很好理解

我们可以把它转化成这样

甲从原点开始,乙也从原点开始,找两条权值最大的路,两条路不能有重合的部分

方向只能向右或向下

这就是一个多进程的dp

我们可以考虑dp方程了

一般就会定为4维   dp[i1][j1][i2][j2]  表示甲走到了i1,j1,乙走到了 i2 j2,能获得的最大价值

看到题目,可以从题目性质考虑一下

它的递推是斜着的

于是可以斜着考虑

每一个状态是由上一条斜线递推出来的

于是

dp方程就可以得出来了

dp[k][i][j]=max(max(dp[k-1][i-1][j],dp[k-1][i][j-1]),max(dp[k-1][i-1][j-1],dp[k-1][i][j]))+d[i][k-i]+d[j][k-j];

k表示一条斜线上的点,因为它的横纵坐标之和是个定值,所以可以用横纵坐标之和表示第一维

第二维表示甲已经走到了第i行

第三维表示乙已经走到了第j行

压了一维之后甲的纵坐标是可以求出来的  就是k-i

乙的纵坐标也可以得出 是 k-j

我们就可以写这个题了

注意

1.一开始将dp赋初值为-1,表示所有点不可达

dp[2][1][1]=0;

2.因为递推时有很多状态是到达不了的,于是就可以continue

最后输出dp[n+m-1][n-1][n]这个值就行了,因为相当于

这里的两个绿点,反正最后一个点的权值为0

上代码

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <cstring>
 6 using namespace std;
 7 const int N=60;
 8 int d[N][N],dp[5*N][N][N];
 9 int n,m;
10 int main()
11 {
12     scanf("%d %d",&n,&m);
13     for(int i=1;i<=n;i++)
14         for(int j=1;j<=m;j++)
15         {
16             scanf("%d",&d[i][j]);
17         }
18     memset(dp,-1,sizeof(dp));
19     dp[2][1][1]=0;
20     for(int k=3;k<n+m;k++)
21         for(int i=1;i<n;i++)
22         {
23             for(int j=i+1;j<=n;j++)
24             {
25                 dp[k][i][j]=max(max(dp[k-1][i-1][j],dp[k-1][i][j-1]),
                     max(dp[k-1][i-1][j-1],dp[k-1][i][j])); 26 if(dp[k][i][j]==-1) 27 continue; 28 dp[k][i][j]+=d[i][k-i]+d[j][k-j]; 29 } 30 } 31 printf("%d\n",dp[n+m-1][n-1][n]); 32 }

这题就愉快的解决了

猜你喜欢

转载自www.cnblogs.com/wzrdl/p/9776910.html