这一题其实思路还是蛮精妙的,向CSP202012-4 食材运输(图论+状压DP)学习。
题目
题目背景
在T市有很多个酒店,这些酒店对于不同种类的食材有不同的需求情况,莱莱公司负责每天给这些酒店运输食材。
由于酒店众多,如何规划运输路线成为了一个非常重要的问题。你作为莱莱公司的顾问,请帮他们解决这个棘手的问题。
题目描述
T市有 N 个酒店,这些酒店由 N−1 条双向道路连接,所有酒店和道路构成一颗树。不同的道路可能有不同的长度,运输车通过该道路所需要的时间受道路的长度影响。
在T市,一共有 K 种主流食材。莱莱公司有 K 辆车,每辆车负责一种食材的配送,不存在多辆车配送相同的食材。
由于不同酒店的特点不同,因此不同酒店对食材的需求情况也不同,比如可能 1 号酒店只需要第 1,5 种食材, 2 号酒店需要全部的 K 种食材。
莱莱公司每天给这些公司运输食材。对于运输第 i 种食材的车辆,这辆车可以从任意酒店出发,然后将食材运输到所有需要第 i 种食材的酒店。假设运输过程中食材的装卸不花时间,运输车足够大使得其能够在出发时就装满全部所需食材,并且食材的重量不影响运输车的速度。
为了提高配送效率,这 K 辆车可以从不同的酒店出发。但是由于T市对于食品安全特别重视,因此每辆车在配送之前需要进行食品安全检查。鉴于进行食品安全检查的人手不足,最多可以设置 M 个检查点。
现在莱莱公司需要你制定一个运输方案:选定不超过 M 个酒店设立食品安全检查点,确定每辆运输车从哪个检查点出发,规划每辆运输车的路线。
假设所有的食材运输车在进行了食品安全检查之后同时出发,请制定一个运输方案,使得所有酒店的等待时间的最大值最小。酒店的等待时间从运输车辆出发时开始计算,到该酒店所有需要的食材都运输完毕截至。如果一个酒店不需要任何食材,那么它的等待时间为 0 。
输入格式
从标准输入读入数据。
输入的第一行包含 3 个正整数 N,M,K (1≤N≤102,1≤M≤K≤10),含义见题目描述。
接下来 N 行,每行包含 K 个整数。每行输入描述对应酒店对每种食材的需求情况, 1 表示需要对应的食材, 0 表示不需要。
接下来 N−1 行,每行包含 3 个整数 u,v,w ,表示存在一条通行时间为 w 的双向道路连接 u 号酒店和 v 号酒店。保证输入数据是一颗树,酒店从 1 编号到 N ,保证 1≤u,v≤N 并且 1≤w≤106。
输出格式
输出到标准输出。
输出一个整数,表示在你的方案中,所有酒店的等待时间的最大值。
思路
首先题目中要找的是所有酒店的等待时间的最大值,等待时间最长的肯定是一条线路里面最后拿到食材的那个酒店,由题意可得,整个线路应该是一个树形,那么找到一个非最后的酒店还需要回退回来,由此可得等待时间最大值应该是树中所有路的长度的两倍减去从出发点到最后一个酒店的距离,这一点可以通过深度优先搜索实现,这样可以得到从每一个酒店出发运送各种食材所需的等待时间最大值,然后使用DP,定义dp[j][ii]
表示使用 j
辆车时,已经运输的食材状态为 ii
的情况是否满足要求。ii
是一个整数,使用位运算表示已经运输的食材,初始化dp[0][0] = 1
表示0辆车时,未运输任何食材是可行的。进行状态更新,dp[j][ii] |= tmp[j][ii]
表示如果不使用当前酒店 i
的话,保持当前状态。dp[j][ii | T[i]] |= tmp[j - 1][ii]
表示如果使用当前酒店 i
,那么我们用 j-1
辆车运输,结合之前的状态,更新状态为 ii | T[i]
,表示新增了从酒店 i
运输的食材。其中T[i]
存储可以从酒店 i
运输的食材状态。整体上是二分法,不断找出在这样等待时间的最大值下,能否满足条件。
代码
#include <bits/stdc++.h>
using namespace std;
int n, m, k;
int r[101];
int flag[101];
int sum;
int G[105][20];
int dp[15][1025];
int temp[15][1025];
int T[101];
typedef struct node{
int v, w;
}node;
vector<node> g[105];
int dfs(int u, int fa) {
int t = 0;
int i;
for(i = 0; i < g[u].size(); i++) {
int v = g[u][i].v;
int w = g[u][i].w;
if(v == fa) continue;
int re = dfs(v, u);
if(flag[v])
{
flag[u] = 1;
sum = sum + w;
t = max(t, w + re);
}
}
return t;
}
int judge(int x)
{
int i, j, ii;
memset(dp, 0, sizeof(dp));
memset(T, 0, sizeof(T));
for(i = 1; i <= n; i++) {
for (j = 0; j < k; j++) {
if(G[i][j] <= x) {
T[i] |= (1 << j);
}
}
}
temp[0][0] = dp[0][0] = 1; //初始化
for(i = 1; i <= n; i++)
{
for(j = 1; j <= m; j++)
{
memcpy(temp, dp, sizeof(dp));
for(ii = 0; ii < (1 << k); ii++)//状态更新
{
dp[j][ii] |= temp[j][ii];
dp[j][ii | T[i]] |= temp[j - 1][ii];
}
}
}
return dp[m][(1 << k) - 1];
}
int main()
{
int i, j;
cin >> n >> m >> k;
for(i = 1; i <= n; i++)
{
for(j = 0; j < k; j++)
{
int x;
cin >> x;
if (x == 1) {
r[i] |= (1 << j);
}
}
}
for(i = 1; i < n; i++)
{
int u, v, w;
cin >> u >> v >> w;
g[u].push_back({ v,w });
g[v].push_back({ u,w });
}
for (i = 1; i <= n; i++)
{
for (j = 0; j < k; j++)
{
memset(flag, 0, sizeof(flag));
for(int ii = 0; ii <= n; ii++) {
if ((r[ii] >> j) & 1) {
flag[ii] = 1;
}
}
sum = 0;
int t = dfs(i, -1);
G[i][j] = sum * 2 - t;
}
}
int l = 1, r = 1e9, ans = r;
while(l <= r)
{
int mid = (l + r) >> 1;
if(judge(mid))
{
r = mid - 1;
ans = mid;
}
else l = mid + 1;
}
cout << ans;
return 0;
}