一个公司有三个移动服务员。如果某个地方有一个请求,某个员工必须赶到那个地方去(那个地方没有其他员工),某一时刻只有一个员工能移动。
被请求后,他才能移动,不允许在同样的位置出现两个员工。从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 ;
}
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 ;
}