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;
}