P1130 红牌
思路如下
这一题很像数字金字塔,我们可以正着求最小时间,当然也可以逆着求最小时间,
- 如果正着求:那么我们怎么求状态转移方程呢?,在这里我们假定状态转移方程为:dp[ i ][ j ] ,i 表示第i行,j表示第j列,dp[ i ][ j ] 则表示从开始点(第一个步)到(i , j)这个位置所需要的最小的时间。当j > 1 && i > 1 的时候,我们考虑(i, j)这个位置是有那个位置转换过来的显然是由(i , j - 1)、(i - 1 , j)两个点走过来的,到底是着这两个点中的哪个点呢?,在这里我们直接通过min函数进行选择就行了,至此还有一些细节需要注意,当 i 表示是最第一行的时候我们要特殊考虑这一行的点是由哪些点转化过来的。
- 如果我们逆着求:其实思路跟正着求解差不多,只不过是逆着求解这个问题而已,那么我们仍然做假设dp[ i ][ j ] ,i 表示第i行,j表示第j列,dp[ i ][ j ] 则表示从(i , j)这个位置到终点(最后一步)所需要的最小的时间。这里当 i > 1 && i <M && j >= 1 && j < N 的时候,我们考虑dp[ i ][ j ] 是由那个状态转移而来,我们可以先考虑(i , j)这个点是经过“下一步”操作可以变成(i , j. + 1)、(i + 1 , j + 1)这两个点,由于是逆向思考,所以dp[ i ][ j ] 这个点的最优值是从dp[ i ][j + 1] 、 dp[i + 1][j + 1] 这两个🀄️选取最小时间再加上 (i , j) 这个点所代表的时间map[ i ][ j ],即最终的状态转移方程为: dp[ i ][ j ] = min(dp[ i ][j + 1] 、 dp[i + 1][j + 1] ) + map[ i ][ j ]. 在理解了这些之后,剩下的就是细节了这个时候我们再考虑这种条件 i == M && j >= 1 && j < N ,其实思路还是一样把状态转移方程稍微改一下就行了:dp[i][j] += (min(dp[i][j + 1],dp[1][j + 1]) + map[i][j]);(自己想一下为什么这样),还剩下最后一处细节,我们应该怎么初始化 dp[][] 数组呢?,这个也自己理解一下吧!!!
题解如下(逆向思考题解)
#include<iostream>
#include<algorithm>
using namespace std;
const int Len = 2005;
int map[Len][Len];
int dp[Len][Len];
int main()
{
//freopen("T.txt","r",stdin);
int n,m;
scanf("%d %d",&n,&m);
for(int i = 1; i <= m; i ++) //输入数据
for(int j = 1; j <= n; j ++)
scanf("%d", &map[i][j]);
for(int i = 1; i <= m; i ++) //dp数组进行初始化
dp[i][n] = map[i][n];
for(int j = n - 1; j >= 1;j --)
for(int i = 1; i <= m; i ++)
{
if(i != m)
{
dp[i][j] += (min(dp[i][j + 1],dp[i + 1][j + 1]) + map[i][j]);
}
else
dp[i][j] += (min(dp[i][j + 1],dp[1][j + 1]) + map[i][j]);
//cout<<dp[i][j]<<endl;
}
int ans = 1e10;
for(int i = 1; i <= m; i ++) //遍历找最小值
ans = min(ans , dp[i][1]);
printf("%d",ans);
return 0;
}