SPOJ-703 Mobile Service
题目传送门
原题面#
描述#
A company provides service for its partners that are located in different towns. The company has three mobile service staff employees. If a request occurs at some location, an employee of the service staff must move from his current location to the location of the request (if no employee is there) in order to satisfy the request. Only one employee can move at any moment. They can move only on request and are not allowed to be at the same location. Moving an employee from location p to location q incurs a given cost \(C(p,q)\). The cost function is not necessarily symmetric, but the cost of not moving is \(0\), i.e. \(C(p,p)=0\). The company must satisfy the received requests in a strict first-come, first-serve basis. The goal is to minimize the total cost of serving a given sequence of requests.
Task
You are to write a program that decides which employee of the service staff is to move for each request such that the total cost of serving the given sequence of requests is as small as possible.
输入格式#
The first line of input contains the number of test cases - \(n\) Test. Each test case contains:
The first line of each test cases contains two integers, \(L\) and \(N\). \(L (3 \le L \le 200)\) is the number of locations and \(N (1 \le N \le 1000)\) is the number of requests. Locations are identified by the integers from \(1\) to \(L\). Each of the next L lines contains L non-negative integers. The jth number in the line \(i+1\) is the cost \(C(i,j)\), and it is less than \(2000\).
The last of each test cases contains \(N\) integers, the list of the requests. A request is identified by the identifier of the location where the request occurs. Initially, the three service staff employees are located at location \(1\), \(2\) and \(3\), respectively.
输出格式#
For each test case write the minimal total cost in a separate line.
参考翻译#
描述#
有一个公司有 \(3\) 个流动员工。任何时刻只有一名员工可以移动,不允许同一位置上有 \(2\) 个及以上员工。
每次移动需要花费,从位置 \(p\) 移动到位置 \(q\) 需要花费 \(c(p,q)\) 的价钱。不移动不需要花费(即 \(c(i,i)=0\) )但不保证 \(c(p,q)=c(q,p)\)。
现在给出 \(N\) 个请求,第 i 个请求发生在位置 \(x_i\)。公司必须按照顺序,派一名员工到位置 \(x_i\),过程中不能去其他地方,也就是必须直接过去。
\(3\) 个流动员工的初始位置分别为 \(1,2,3\)。
求公司的最小花费。
输入格式#
第一行一个数 \(T\) ,表示数据组数。
每组数据的第一行有两个数 \(L,N\),表示有 \(L\) 个位置和 \(N\) 个请求。
接下来的 \(L\) 行中的每一行都包含 \(L\) 个非负整数。其中第 \(i+1\) 行第 \(j\) 个数是 \(c(i,j)\),表示价钱。
最后一行,有 \(n\) 个整数,分别为 \(x_1,x_2,x_3 \dots ~ x_n\) 表示请求的位置。
输出格式#
一行一个数,表示每组数据的最小花费。
数据范围与说明#
对于 \(100\%\) 的数据,保证 \(3 \le L \le 200\),\(1 \le N \le 1000\),\(0 \le c(i,j) \le 2000\)。
样例#
样例输入#
1
5 9
0 1 1 1 1
1 0 2 3 2
1 1 0 4 1
2 1 5 0 1
4 2 3 4 0
4 2 4 1 5 4 3 2 1
样例输出#
5
题解#
分析题目,可以列出状态:\(f[i][A][B][C]\) 表示对于第 \(i\) 个请求,三名员工的位置分别为 \(A,~B,~C\) 时的最小花费。
四维 \(DP\) 一看就是直接 \(MLE+TLE\)。
那么再仔细分析,我们可以得出:
- \(f[i]\) 中的值只取决于 \(f[i-1]\) 中的值,故可以用两个数组来滚动。
- 对于第 \(i\) 个请求,必定是有且只有一个员工是位于 \(query[i]\) 的位置的,这就可以干掉一维。
所以最后我们的状态为:\(f[i][j][k]\) 表示对于第 \(i\) 个请求,两名不在\(query[i]\) 位置的员工分别位于 \(i\),\(j\) 时的最小花费。
一旦确定了状态是什么,转移方程也就好求了。
经过一顿乱搞严密的思考,我们可以得出如下 \(3\) 个状态转移方程:
设 \(x = query[i-1],~y=query[i]\),则有:
- \(f[i][B][C] = \min(f[i][B][C],~f[i-1][B][C]+c[x][y])\)
- \(f[i][B][x] = \min(f[i][B][x], f[i-1][B][C]+c[C][y])\)
- \(f[i][C][x] = \min(f[i][C][x], f[i-1][B][C]+c[B][y])\)
因为在状态 \(f[i][j][k]\) 中,营业员的位置是无序的,所以理论上 \(f[i][j][k]=f[i][k][j]\)(事实上也是)。
上代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define L 210
#define N 1010
using namespace std;
int q, l, n, c[L][L], f[2][L][L], query[N];
// f[p][i][j]表示有两个业务员分别在位置i,j(另一名的位置可以通过上一轮请求确定)时的最小花费。
int main()
{
scanf("%d", &q);
while(q--)
{
scanf("%d%d", &l, &n);
for(int i = 1; i <= l; i++) for(int j = 1; j <= l; j++) scanf("%d", &c[i][j]);
for(int i = 1; i <= n; i++) scanf("%d", &query[i]);
memset(f, 0x3f, sizeof(f));
f[1][1][2] = f[1][2][1] = c[3][query[1]];
f[1][1][3] = f[1][3][1] = c[2][query[1]];
f[1][2][3] = f[1][3][2] = c[1][query[1]];
for(int i = 2, t; i <= n; i++)
{
t = i & 1;
memset(f[t], 0x3f, sizeof(f[t]));
for(int j = 1; j <= l; j++)
{
if(j == query[i-1]) continue;
for(int k = 1, x, y; k <= l; k++)
{
if(k == query[i-1] || j == k) continue;
x = query[i-1], y = query[i];
f[t][j][k] = f[t][k][j] = min(f[t][j][k], f[t^1][j][k]+c[x][y]);
f[t][j][x] = f[t][x][j] = min(f[t][j][x], f[t^1][j][k]+c[k][y]);
f[t][k][x] = f[t][x][k] = min(f[t][k][x], f[t^1][j][k]+c[j][y]);
}
}
}
int ans = 1<<30;
for(int i = 1; i <= l; i++)
{
if(i == query[n]) continue;
for(int j = 1; j <= l; j++)
{
if(i == j || j == query[n]) continue;
ans = min(ans, f[n&1][i][j]);
}
}
printf("%d\n", ans);
}
return 0;
}
作者:chy12321
出处:https://www.cnblogs.com/chy12321/p/14844912.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现