Description
要完成一个由s个子项目组成的项目,给b(b>=s)个部门分配,从而把b个部门分成s个组。分组完成后,每一组的任意两个点之间都要传递信息。假设在(i,j)两个点间传送信息,要先把信息加密,然后快递员从i出发到总部,再加密,在到j点。出于安全原因,每次只能携带一条消息。现在给出了道路网络、各个部门和总部的位置,请输出快递员要走的最小总距离。
Input
第一行包含四个整数n,b,s,r。n(2<=n<=5000)代表路口数,b(1<=b<=n-1)是部门数,s(1<=s<=b)是子项目数
r(1<=r<=50000)是道路数。路口标号为1~n,部门在路口1~b,总部在路口b+1。
接下来r行每行三个整数u,v,l,描述一条从u到v长度为l(0<=l<=10000)的单向边。保证没有重边,保证图强连通。
Output
输出快递员要走的最小总距离。
Sample Input
5 4 3 8
1 5 15
5 1 15
2 5 2
5 2 3
3 5 1
5 3 1
4 5 2
5 4 0
Sample Output
4
解题思路:
首先肯定是正向反向两遍SPFA处理出每个点到总部的距离和总部到每个点的距离。
如果某个点所在的部门的大小为k,那么这个点需要送出k-1次消息并接收k-1次消息。
我们把每个点的两个距离求和并排序,显然在一个块中的是这个序列上的一个区间,我们做一下前缀和。设
f[i][j]
f
[
i
]
[
j
]
表示前i个点分j个块,最小代价。那么
直接dp是 O(n3) O ( n 3 ) 的,但注意随着j增大,对于每个 i i 最优的 也是递增的,直接指针扫过去,复杂度 O(n2) O ( n 2 ) 。
要开滚动数组。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=5005,M=50000,inf=1e9;
int n,b,s,m,tp[N];ll f[2][N],sum[N];
struct Graph
{
int tot,first[N],nxt[M],to[M],w[M],dis[N],exist[N];
queue<int>q;
void add(int x,int y,int z)
{
nxt[++tot]=first[x],first[x]=tot,to[tot]=y,w[tot]=z;
}
void spfa()
{
for(int i=1;i<=n;i++)dis[i]=inf;
dis[b+1]=0,q.push(b+1),exist[b+1]=1;
while(!q.empty())
{
int u=q.front();q.pop(),exist[u]=0;
for(int e=first[u];e;e=nxt[e])
{
int v=to[e];
if(dis[v]>dis[u]+w[e])
{
dis[v]=dis[u]+w[e];
if(!exist[v])q.push(v),exist[v]=1;
}
}
}
}
}G,R;
int main()
{
//freopen("lx.in","r",stdin);
n=getint(),b=getint(),s=getint(),m=getint();
while(m--)
{
int x=getint(),y=getint(),z=getint();
G.add(x,y,z),R.add(y,x,z);
}
G.spfa(),R.spfa();
for(int i=1;i<=b;i++)sum[i]=G.dis[i]+R.dis[i];
sort(sum+1,sum+b+1);
for(int i=1;i<=b;i++)sum[i]+=sum[i-1];
memset(f[0],0x3f,sizeof(f[0])),f[0][0]=0;
for(int j=1,cur=1;j<=s;j++,cur^=1)
{
memset(f[cur],0x3f,sizeof(f[1]));
for(int i=1;i<=b;i++)
for(int lst=tp[i];lst<i;lst++)
if( f[cur][i] >= f[cur^1][lst] + ( i - lst - 1 ) * ( sum[i] - sum[lst] ) ) f[cur][i] = f[cur^1][lst] + ( i - lst - 1 ) * ( sum[i] - sum[lst] ) , tp[i] = lst;
}
cout<<f[s&1][b]<<'\n';
}