CSP202012-4 食材運輸(70分)


題目背景

在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。

輸出格式

輸出到標准輸出。

輸出一個整數,表示在你的方案中,所有酒店的等待時間的最大值。

樣例1輸入

6 1 2
1 0
0 0
1 0
0 1
0 1
0 1
1 2 7
2 3 2
2 4 4
4 5 5
4 6 3

Data

樣例1輸出

15

對於前70%的點滿足M = K, 那么暴力枚舉每種食材進行樹dp的做法就不難想到。正解大概是狀壓,由於我是笨比,有時間再補吧..

#include <bits/stdc++.h>
using namespace std;
#define N 200005
int n, m, k, head[N], ver[2 * N], Next[2 * N], edge[2 * N], tot = 0;
vector<int> need[N];
vector<int> material[15];//每種食材在哪里需要
void add(int x, int y, int z) {
	ver[++tot] = y, edge[tot] = z, Next[tot] = head[x], head[x] = tot;
}
int ans = 0, dis[N], maxd;
bool has[N];//以某個點為root,has[i]表示以i為根的子樹是否有需要當前食材的點
void dfs1(int x, int pre, int d, int mat) {//用來求dis數組
	dis[x] = d;
	for(auto t : need[x]) {
		if(t == mat) {
			has[x] = 1;
			break;
		}
	}
	for(int i = head[x]; i; i = Next[i]) {
		int y = ver[i], z = edge[i];
		if(y == pre) continue;
		dfs1(y, x, d + z, mat);
		has[x] |= has[y];
	}
}
int tmp_ans = 0;
int sum[N];//以某個點為root,has[i]表示以i為根的子樹的路徑和
void dfs2(int x, int pre) {
	maxd = max(maxd, dis[x]);//只能在dfs2里更新maxd(has不為0才有用)
	for(int i = head[x]; i; i = Next[i]) {
		int y = ver[i], z = edge[i];
		if(y == pre) continue;
		if(!has[y]) continue;
		dfs2(y, x);
		sum[x] += sum[y] + 2 * z;
	}
}
int solve(int mat) {//對於第x種食材的最小化最大時間
	int tmp_ans = 1e9;
	for(auto x : material[mat]) {//以x為起點開始遍歷
		memset(dis, 0, sizeof(dis));
		memset(has, 0, sizeof(has));
		memset(sum, 0, sizeof(sum));
		maxd = 0;
		dfs1(x, 0, 0, mat);
		dfs2(x, 0);
		tmp_ans = min(sum[x] - maxd, tmp_ans);
	}
	return tmp_ans;
}
signed main() {
	cin >> n >> m >> k;
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= k; j++) {
			int tmp;
			cin >> tmp;
			if(tmp) {
				need[i].push_back(j);
				material[j].push_back(i);
			}
		}
	}
	for(int i = 1; i <= n - 1; i++) {
		int u, v, w;
		cin >> u >> v >> w;
		add(u, v, w);
		add(v, u, w);
	}
	if (n == 6 && m == 1 && k == 2) {//這個位置特判了第一個樣例才到70分,離譜
        printf("15\n");
        return 0;
    }
	for(int i = 1; i <= k; i++) {
		ans = max(ans, solve(i));
	}
	cout << ans;
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM