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

Description

A君与B君正在玩一款闯关游戏,游戏共有n关,每一关的目标只有一个:开房间。
每一关都会有m个房间(从1~m进行编号),A君与B君每关各打开一个房间即可过关,但两人不能打开同一个房间。
通过每一关后,m个房间会重新关上,在第i关打开第j个房间需要消耗t[i][j]的体力值。并且无论A君还是B君,除了第一关外,若上一关自己开了a号房间,这一关开了b号房间,则需要额外消耗K*|a-b|点体力值。
现在请你回答,两人过完全部n关后,所要消耗的体力值之和(两人消耗体力相加)最小能是多少。

Solution

直接60分DP的f[i][j][k]很明显。
但是要如何有优美的转移,我们知道在同一层走一个是k,但是我们在同一层 由(i-1,j),(i,j-1),(i+1,j),(i,j+1)转移过来,每次转移的时候+k。
那么这里只用 m2 就可以了
然后我们在最后再用a[i][j]加上每层开门的贡献就好了。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=307;
int i,j,k,l,t,n,m,ans,pan,K,o;
int f[maxn][maxn][maxn],a[maxn][maxn];
int jue[maxn*2];
int main(){
    freopen("room.in","r",stdin);
    freopen("room.out","w",stdout);
//    freopen("fan.in","r",stdin);
    scanf("%d%d%d",&n,&m,&K);
    fo(i,1,n)fo(j,1,m)scanf("%d",&a[i][j]);
    memset(f,127,sizeof(f));pan=f[0][0][0];
    fo(i,1,m)fo(j,i+1,m)f[1][i][j]=a[1][i]+a[1][j];
    fo(i,1,n)ans+=a[i][1]+a[i][2];
    fo(i,2,n){
        fo(j,1,m)fo(k,1,m)f[i][j][k]=f[i-1][j][k];
        fo(j,1,m)fo(k,j+1,m)f[i][j][k]=min(f[i][j-1][k]+K,f[i][j][k]);
        fod(j,m,1)fo(k,j+1,m)f[i][j][k]=min(f[i][j+1][k]+K,f[i][j][k]);
        fo(j,1,m)fo(k,j+1,m)f[i][j][k]=min(f[i][j][k-1]+K,f[i][j][k]);
        fo(j,1,m)fod(k,m,j+1)f[i][j][k]=min(f[i][j][k+1]+K,f[i][j][k]);
        fo(j,1,m)fo(k,j+1,m)if(j!=k)f[i][j][k]+=a[i][j]+a[i][k];
    }
    fo(i,1,m)fo(j,i+1,m)if(i!=j)ans=min(ans,f[n][i][j]);
    printf("%d\n",ans);
}

你可能感兴趣的:(DP,省选)