Description
A君与B君正在玩一款闯关游戏,游戏共有n关,每一关的目标只有一个:开房间。
每一关都会有m个房间(从1~m进行编号),A君与B君每关各打开一个房间即可过关,但两人不能打开同一个房间。
通过每一关后,m个房间会重新关上,在第i关打开第j个房间需要消耗t[i][j]的体力值。并且无论A君还是B君,除了第一关外,若上一关自己开了a号房间,这一关开了b号房间,则需要额外消耗K*|a-b|点体力值。
现在请你回答,两人过完全部n关后,所要消耗的体力值之和(两人消耗体力相加)最小能是多少。
Input
第一行三个整数n,m,K.
接下来n行每行m个整数,第i行第j个整数t[i][j]表示第i关开第j个房间需要消耗的体力值。
Output
仅一行一个整数表示答案。
Sample Input
3 3 10
2 13 4
4 3 2
16 4 3
Sample Output
28
Data Constraint
30%的数据:n,m <= 5
60%的数据:n,m <= 50
100%的数据:1 <= n,m <= 300 , 1 <= K,t[i][j] <= 10^6
心情复杂,打了半天的题解死机一下全没了。。。
这题我讲一下思考过程,不想听的可以跳过。
首先我们很容易想到列出f[i][j][k]表示已经走完前i-1层,走到第i层,A走j,B走k的方案数。然后我们先无脑暴力一波,直接枚举从上一层的j,k跳到这层的j'k',复杂度nm^4。60分都没有,明显不行。那么我们就致力于减少枚举的次数,最好能达到nm^2。
要减少,也就是说,我们目前枚举的有两个数是没用的,或者能替代掉,才能去。那么,对于上一层的位置j,k,明显是不能去掉的的,因为我们并不能确定当前位置的上一个状态。那么我们只能去掉对于当前第i层的枚举了。怎么替代呢,我们想想,如果我们必须枚举这一层,是因为我们不能确定,从上一层的j,k位置,走最优方案,到这一层是哪一个位置。但是明显,我们是可以不用确认的,因为我们对于每一个位置j',更新j',j'+1,j'-1(下一层),就可以把状态算上,
这什么意思呢?我举个例子:比如我们一开始更新了f[i][1][2],(j'=1,k'=2)f[i][1][3].....(i!=1),然后我们更新到f[i][1][3](j'=2,k'=3),此时更新的时候,已经把上一个状态累积进去了,用这种方法,我们就可以成功将dp复杂度缩小到nm^2.
所以容易得dp式子
f[i][j+1][k]=min(f[i][j+1][k],f[i][j][k]+c)(c即是参数,j
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
ll inf=1e15;
int n,m;
const int N=305;
ll f[N][N];
int a[N][N];
int c;
int main()
{
freopen("room.in","r",stdin);
freopen("room.out","w",stdout);
scanf("%d%d%d",&n,&m,&c);
fo(i,1,n)
{
fo(j,1,m)
scanf("%d",&a[i][j]);
}
fo(i,1,m)
fo(j,1,i-1)f[i][j]=a[1][i]+a[1][j];
fo(i,2,n)
{
//memcpy(f[i],f[i-1],sizeof(f[i]));
fo(j,1,m)
fo(k,1,j-1)
{
if (j+1<=m)
f[j+1][k]=min(f[j+1][k],f[j][k]+c);
if (k+1<=m)
f[j][k+1]=min(f[j][k+1],f[j][k]+c);
}
fd(j,m,1)
fd(k,j-1,1)
{
if (j-1>=1)
f[j-1][k]=min(f[j-1][k],f[j][k]+c);
if (k-1>=1)
f[j][k-1]=min(f[j][k-1],f[j][k]+c);
}
fo(j,1,m)
fo(k,1,j-1)
f[j][k]+=a[i][j]+a[i][k];
}
ll ans=inf;
fo(i,1,m)
fo(j,1,i-1)ans=min(ans,f[i][j]);
printf("%lld\n",ans);
}