[bzoj4919] [Wf2016]Branch Assignment

本文介绍了一种结合最短路径算法与动态规划的优化方案,用于解决特定类型的组合优化问题。通过预处理每个点到总部的距离,利用调整法证明最优解特性,排序并计算前缀和,进而设计高效的DP转移方程。通过限制转移范围实现复杂度从O(n^3)优化至O(n^2logn),并提供完整代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

思路

先正反两遍最短路预处理每个点iii到总部的距离和总部到iii的距离之和valival_{i}vali
如果iii所在组大小为kkk,则贡献为i∗(k−1)i*(k-1)i(k1)
显然,调整法可以证明,最优解肯定是权值大小相邻的在同一组,所以排序后求出valival_{i}vali的前缀和sumisum_{i}sumi就可以dp了。
可以写出转移方程:
dp[i][k]=min(dp[j][k−1]+(sum[i]−sum[j])∗(i−j−1))(j&lt;i)dp[i][k]=min(dp[j][k-1]+(sum[i]-sum[j])*(i-j-1)) (j&lt;i)dp[i][k]=min(dp[j][k1]+(sum[i]sum[j])(ij1))(j<i)
直接转移是O(n3)O(n^{3})O(n3)的,然而我们可以发现,因为valival_{i}vali是从小到大排序的,所以每段的长度是单调不增的,所以jjj的取值是在[i−i/k,i)[i-i/k,i)[ii/k,i)之间,这是个调和级数求和,这样复杂度就优化到了O(n2logn)O(n^{2}log_{n})O(n2logn)了。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define ll long long
#define maxn 5050
#define maxm 50050
using namespace std;
int n,b,s,m,tu[maxm],tv[maxm],tl[maxm];
void init(){
	scanf("%d%d%d%d",&n,&b,&s,&m);
	for(int i=1;i<=m;++i)scanf("%d%d%d",&tu[i],&tv[i],&tl[i]);
}
int tot,la[maxn];
struct edge{int v,ne,l;}e[maxm];
inline void add(int u,int v,int l){
	e[tot].v=v,e[tot].ne=la[u],e[tot].l=l,la[u]=tot++;
}
ll d[maxn],val[maxn];
struct node{ll d;int id;};
inline bool operator<(const node &a,const node &b){return a.d>b.d;}
priority_queue<node>qu;
void dij(int s){
	while(!qu.empty())qu.pop();
	memset(d,127/2,sizeof(d));
	d[s]=0;qu.push((node){0,s});
	while(!qu.empty()){
		int u=qu.top().id;ll du=qu.top().d;qu.pop();
		if(du>d[u])continue;
		for(int i=la[u];~i;i=e[i].ne){
			int v=e[i].v,l=e[i].l;
			if(du+l<d[v]){
				d[v]=du+l;
				qu.push((node){d[v],v});
			}
		}
	}
}
void getval(){
	memset(val,0,sizeof(val));
	tot=0;memset(la,-1,sizeof(la));
	for(int i=1;i<=m;++i)add(tu[i],tv[i],tl[i]);
	dij(b+1);
	for(int i=1;i<=b;++i)val[i]+=d[i];
	tot=0;memset(la,-1,sizeof(la));
	for(int i=1;i<=m;++i)add(tv[i],tu[i],tl[i]);
	dij(b+1);
	for(int i=1;i<=b;++i)val[i]+=d[i];
	sort(val+1,val+b+1);
}
ll dp[2][maxn];
void solve(){
	getval();
	for(int i=2;i<=b;++i)val[i]+=val[i-1];
	memset(dp[0],127/2,sizeof(dp[0]));
	dp[0][0]=0;
	for(int k=1;k<=s;++k){
		int now=k&1,lst=now^1;
		memset(dp[now],127/2,sizeof(dp[now]));
		for(int i=1;i<=b;++i)
		for(int j=i-i/k;j<i;++j)
		dp[now][i]=min(dp[now][i],dp[lst][j]+(val[i]-val[j])*(i-j-1));
	}
	printf("%lld\n",dp[s&1][b]);
}
int main(){
	init(); 
	solve();
	return 0;
}

update

当然神犇可以用凸优化+决策单调性O(nlogn2)O(nlogn^{2})Onlogn2)轻松切掉此题辣。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值