Mobile Service ( tyvj P1096 ) 题解

一个公司有三个移动服务员。如果某个地方有一个请求,某个员工必须赶到那个地方去(那个地方没有其他员工),某一时刻只有一个员工能移动。

被请求后,他才能移动,不允许在同样的位置出现两个员工。从p到q移动一个员工,需要花费c(p,q)。这个函数没有必要对称,但是c(p,p)=0。

公司必须满足所有的请求。目标是最小化公司花费。

具体解析我就很不客气的贴上tyvj上的解答了:

一个很显然的想法.
用f[i,x1,x2,x3]表示第i时刻三个人的位置是x1,x2,x3(并不是三个人分别在).用p[i]表示i时刻的请求,用map[i,j]表示把人从位置i感到位置j的代价.
则可以得到一个很显然递推式:
f[i,p[i],x2,x3]:=f[i,x1,x2,x3]+map[x1,p[i]];意思就是在i时刻,把位置x1上的人赶到位置p[i]去.
同理可得其它两个递推式.

但是,这样做的空间是1000*200*200*200,就算加上滚动数组也是2*200*200*200,这样的程序绝对是能把评测机搞坏的存在.

题目中有这么一句话:如果某个地方有一个请求,某个员工必须赶到那个地方去。也就是说,i时刻结束后,三个人里必定有一个人的位置是p[i]的.或者说,在i时刻开始前,一定有一个人的位置是p[i-1];

这样的话,可以压缩掉一维。

用f[i,x1,x2]表示第i时刻,三个人的位置分别是x1,x2,p[i].可得以下的递推式:
f[i,x2,p[i-1]]:=f[i-1,x1,x2]+map[x1,p[i]];
f[i,x1,p[i-1]]:=f[i-1,x1,x2]+map[x2,p[i]];
f[i,x1,x2]:=f[i-1,x1,x2]+map[p[i-1],p[i]];

可能比较难理解,我解释一下.以第一个为例,第i-1时刻,三个人的位置是x1,x2,p[i-1]即f[i-1,x1,x2],我们把位于x1的人赶到p[i]去,产生map[x1,p[i]]的费用.三个人的位置变为x2,p[i-1],p[i]即f[i,x2,p[i-1]]1.所以可得上面那个递推式.其它两个类似.

这样的话,加上滚动数组之后,空间变为2*200*200,这样就很优了.

边界的话,一开始三个服务员分别在位置1,2,3.所以,先读入p[1],边界就是f[1,1,2]:=map[3,p[1]];f[1,1,3]:=map[2,p[1]];f[1,2,3]:=map[1,p[1]];

这个题就是这样.

代码自己写的,用了滚动数组。

 
  
/*
keep it in mind:
这题是当前状态推出后面的状态(以前做的都是由以前的状态来推当前状态)

当一个状态可以由少数过去状态推出,而可以推出许多未来状态的时候 可以用 前推后
当状态可以产生少数的后状态,而可以由许多以前的状态推出的时候 可以用 后退前

这一题还有一个亮点就是压缩。
对于一个状态和方程我们要尽可能的想这个状态是否有冗余,或者有一定的同性
本状态就有一个重要的同性就是每秒结束后总有一个人在GO[I]
所以我们可以根据这一同性进行状态的压缩
F[I,X1,X2] x1.x2代表不在P[I]的那两个人的位置
此状态就默认了第四维X3始终处于P[I]
这样就成功的完成了压缩

压缩状态~~多多寻找原来状态的通性
*/
// 2011-4-30 Mobile Service (DP) tyvj P1096 pass Accepted / 100 / 131ms / 736KB
#include < iostream >
#include
< assert.h >
#include
< stdlib.h >
using namespace std;

// 滚动数组
#define MOD(a) ((a)&1)
#define MIN(a,b) ((a)<(b)?(a):(b))

const int _D_MAXL = 210 ;
const int _D_MAXNUM = 0x7fffffff ;

int _gord,_gl,_gmap[_D_MAXL][_D_MAXL];
int _gdp[ 2 ][_D_MAXL][_D_MAXL];

void _finit()
{
cin
>> _gl >> _gord;
for ( int i = 1 ;i <= _gl; ++ i )
for ( int j = 1 ;j <= _gl; ++ j )
cin
>> _gmap[i][j];
for ( int i = 0 ;i < 2 ; ++ i )
for ( int j = 1 ;j <= _gl; ++ j )
for ( int k = 1 ;k <= _gl; ++ k )
_gdp[i][j][k]
= _D_MAXNUM;
}

int main()
{
// system("pause");
_finit();
int no,lo;
_gdp[MOD(
0 )][ 1 ][ 2 ] = 0 ;
lo
= 3 ;
for ( int i = 0 ;i < _gord; ++ i )
{
cin
>> no;
for ( int p1 = 1 ;p1 <= _gl; ++ p1 )
for ( int p2 = 1 ;p2 <= _gl; ++ p2 )
{
if ( _gdp[MOD(i)][p1][p2] == _D_MAXNUM ) continue ;
if ( no != p2 && no != lo ) // p1移动 判断避免两个人在同一个位置
_gdp[MOD(i + 1 )][lo][p2] = MIN(_gdp[MOD(i + 1 )][lo][p2],_gdp[MOD(i)][p1][p2] + _gmap[p1][no]);
if ( no != p1 && no != lo )
_gdp[MOD(i
+ 1 )][lo][p1] = MIN(_gdp[MOD(i + 1 )][lo][p1],_gdp[MOD(i)][p1][p2] + _gmap[p2][no]);
if ( no != p1 && no != p2 )
_gdp[MOD(i
+ 1 )][p1][p2] = MIN(_gdp[MOD(i + 1 )][p1][p2],_gdp[MOD(i)][p1][p2] + _gmap[lo][no]);
_gdp[MOD(i)][p1][p2]
= _D_MAXNUM; // 滚动数组注意擦屁股
}
lo
= no;
}

int ans = _D_MAXNUM;
for ( int i = 1 ;i <= _gl; ++ i )
for ( int j = 1 ;j <= _gl; ++ j )
ans
= MIN(ans,_gdp[MOD(_gord)][i][j]);
cout
<< ans << endl;
return 0 ;
}

转载于:https://www.cnblogs.com/code-/articles/2033592.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值