jzoj【GDOI2017第二轮模拟day2】开房间

92 篇文章 1 订阅
73 篇文章 0 订阅

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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值