P6918 [ICPC2016 WF] Branch Assignment

P6918 [ICPC2016 WF] Branch Assignment

思路

可以建正图和反图先求出每个部门到总部的最短路和总部到每个部门的最短路,这里可以直接跑正图和反图从总部出发的单源最短路。然后对所有部门的耗费,也就是 \(dis(x,b+1)+dis(b+1,x)\) 进行一个排序,然后我们就可以用 dp 来分组。设 \(dp_{i,j}\) 为前 \(i\) 个人中分成 \(j\) 组的最小花费,再枚举一个 \(k\) 表示从 \(dp_{k,j-1}\) 转移过来,也就是 \([k+1,i)\) 分为一组,其中相比于 \(dp_{k,j-1}\) 多出来的花费就是\(\sum_{i=k+1}^{n}{(dis(x,b+1)+dis(b+1,x))\times (i-k)}\)。

优化

仔细算一下上面 \(dp\) 的时间复杂度,会发现它一眼都不能多看,所以我们要对其进行一些优化防止爆炸。首先,转移时多出来的贡献可以预处理出一个前缀和数组来优化,式子就变成了 \(dp_{i,j}=\min(dp_{i,j},dp_{k,j-1}+(sum_i-sum_k)\times (i-k)\)。还有,\(k\) 的枚举范围只能在 \([i-\frac{i}{j},i-1]\) 里面,假如从 \(1\) 开始枚举就会时间复杂度就会爆炸。其实也可以用四边形不等式优化,但本蒟蒻不会。

ACcode

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define double long double
#define endl '\n'
#define inf 0x3f3f3f3f3f3f3f3f
const int N=5001;
int n,b,s,m,mn,sum[N],d1[N],d[N],dp[N][N],val[N];
bool vis[N],vis2[N];
struct road{
	int v,w;
};vector<road> adj[N],jda[N];

void Dij(int s) {
	memset(d, 0x3f, sizeof(d));
	d[s] = 0;
	priority_queue<pair<int,int> , vector<pair<int,int> >, greater<pair<int,int> > > pq;
	pq.push({0, s});
	while (!pq.empty()) {
		pair<int,int>  p = pq.top();
		pq.pop();
		int u = p.second;
//		if (d[u] < p.first) continue;
		if(vis[u]) continue;
		vis[u] = true;
		for (unsigned int i = 0; i < adj[u].size(); i++) {
			int v = adj[u][i].v, w = adj[u][i].w;
			if (d[u] + w < d[v]) {
				d[v] = d[u] + w;
				pq.push({d[v], v});
			}
		}
	}
}
void Dji(int s) {
	memset(d1,0x3f,sizeof(d1));
	d1[s] = 0;
	priority_queue<pair<int,int> , vector<pair<int,int> >, greater<pair<int,int> > > pq;
	pq.push({0, s});
	while (!pq.empty()) {
		pair<int,int>  p = pq.top();
		pq.pop();
		int u = p.second;
//		if (d1[u] < p.first) continue;
		if(vis2[u]) continue;
		vis2[u] = true;
		for (unsigned int i = 0; i < jda[u].size(); i++) {
			int v = jda[u][i].v, w = jda[u][i].w;
			if (d1[u] + w < d1[v]) {
				d1[v] = d1[u] + w;
				pq.push({d1[v], v});
			}
		}
	}
}

signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
// 	freopen("assignment.in","r",stdin);
// 	freopen("assignment.out","w",stdout);
//↑以上为初始化 ↑
//------------------------------
	cin >> n >> b >> s >> m;
	for(int i = 1;i<=m;i++){
		int u,v,w;
		cin >> u >> v >> w;
		adj[u].push_back({v,w});
		jda[v].push_back({u,w});
	}
	Dij(b + 1);
	Dji(b + 1);
	for(int i = 1;i<=b;i++) {
		val[i]=d[i]+d1[i];
	}
	sort(val+1,val+b+1);
	for(int i = 1;i<=b;i++) sum[i]=sum[i-1]+val[i];
	memset(dp,0x3f,sizeof(dp));
	dp[0][0]=0;
	for(int j = 1;j<=s;j++){
		for(int i = 1;i<=b;i++){
			for(int k = i-i/j;k<i;k++){
				dp[i][j]=min(dp[i][j],dp[k][j-1]+(sum[i]-sum[k])*(i-k));
			}
		}
	}
	cout << dp[b][s]-sum[b] << endl;
	return 0;
}
posted @ 2026-10-21 21:32  elon_giao  阅读(3)  评论(0)    收藏  举报