分析:L个位置,N个请求,让我们求最小花费
影响花费因素:处理花费的服务员所在的位置
限制条件
- 过程中不能去其他的位置,只能去发生请求的位置
- 不允许两个员工在同一个位置
划分依据:由于有3个服务员,对于每个请求来说,有三种情况
- 服务员1去服务
- 服务员2去服务
- 服务员3去服务
本题的数据范围是200,对于难做的问题,可以思考暴力枚举怎么做,然后再对枚举进行优化
枚举:
有N个请求,3种选择,分别枚举,有3^N种方案
动态规划:
有N个请求,每个请求时x,y,z的位置范围为[3,200]
而每个请求让x,y,z去做带来的花费影响是来自于 x,y,z此时的位置
枚举会记住一个位置内的所有方案,而用dp算法,我们可以贪心一下,只保存这个位置内最小的花费总和,这样子不会有后效性,能够得到全局最优解
状态表示:dp[i][x][y][z]
含义:处理了前i个选择,此时服务员1在x,服务员2在y,服务员3在z的最小花费s
集合:所有处理了前i个选择,此时服务员1在x,服务员2在y,服务员3在z的集合
属性:min
状态计算:本题根据拓扑排序,可以分为3种方向递推,u=p[i+1]
- 分给x做 dp[i+1][u][y][z]=min(dp[i+1][u][y][z],v+c[x][u]);
- 分给y做 dp[i+1][x][u][z]=min(dp[i+1][x][u][z],v+c[y][u]);
- 分给z做 dp[i+1][x][y][u]=min(dp[i+1][x][y][u],v+c[z][u]);
初始化:全为正无穷 dp[0][1][2][3]=0;
for(int i=0;i<n;i++)
for(int x=1;x<=l;x++)
for(int y=1;y<=l;y++)
for(int z=1;z<=l;z++)
{
int v=dp[i][x][y][z];
if(x==y||x==z||y==z)
continue;
//由于是正拓扑排序,所以i时候是做第i+1个选择
int u=p[i+1];
//分给x做
dp[i+1][u][y][z]=min(dp[i+1][u][y][z],v+c[x][u]);
//分给y做
dp[i+1][x][u][z]=min(dp[i+1][x][u][z],v+c[y][u]);
//分给z做
dp[i+1][x][y][u]=min(dp[i+1][x][y][u],v+c[z][u]);
}
优化
以上的方法空间时间复杂度均为O(n l^3),毫无疑问太高了,思考有没有可以降维的方法
猜测,如果能够减掉一维,只枚举两个服务员位置,另一个服务员和这两个服务员竞争上岗
状态计算:
这里状态之间的拓扑关系比较特殊,f[i][x][y]所依赖的状态枚举起来不太方便,但f[i][x][y]被依赖的很容易枚举,只有3类:
- 位于p[i]的服务员出发前往p[i + 1],此时状态变成f[i + 1][x][y] = f[i][x][y] + w[p[i]][p[i + 1]];
- 位于x的服务员出发前往p[i + 1],此时状态变成f[i + 1][p[i]][y] = f[i][x][y] + w[x][p[i + 1]];
- 位于y的服务员出发前往p[i + 1],此时状态变成f[i + 1][x][p[i]] = f[i][x][y] + w[y][p[i + 1]];
证明:
不同服务员在同一位置去往另外一个位置的消费都是一样的,这里相当于把z和x调换了一下位置,其实还是可以看作都是z去做的,由于x,y,z的坐标都只会在初始点和请求集合之中,所以z的坐标只会是1,2,3和p[i]
完整代码
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=1005;
const int M=205;
int c[M][M];
int p[N];
int dp[N][M][M];
int main()
{
int l,n;
cin>>l>>n;
for(int i=1;i<=l;i++)
for(int j=1;j<=l;j++)
cin>>c[i][j];
for(int i=1;i<=n;i++)
cin>>p[i];
memset(dp,0x3f,sizeof dp);
dp[0][1][2]=0;
p[0]=3;
for(int i=0;i<n;i++)
for(int x=1;x<=l;x++)
for(int y=1;y<=l;y++)
{
int z=p[i];
if(x==y||y==z||x==z) continue;
int v=dp[i][x][y];
int u=p[i+1];
//派x去做
dp[i+1][p[i]][y]=min(dp[i+1][p[i]][y],v+c[x][u]);
//派y去做
dp[i+1][x][p[i]]=min(dp[i+1][x][p[i]],v+c[y][u]);
//派z去做
dp[i+1][x][y]=min(dp[i+1][x][y],v+c[z][u]);
}
int res=0x3f3f3f3f;
for(int x=1;x<=l;x++)
for(int y=1;y<=l;y++)
res=min(res,dp[n][x][y]);
cout<<res;
}