E. Weights Distributing
思路
-
题意:给了我们一个有n个节点m无权值的边形成的无向图,又给了我们 m个权值,我们要把这个m个权值一一分配的图中的m条边上,问一个人从节点a出发,经过节点b,最终到达节点c,经过我合理分配m条边的权值之后,整个过程所需要的最小边权和是多少?
-
思路
- 首先容易看出来的最后的边权和与走的边数有关,那么走的边越少且a->b 与 b->c这两个过程中重复的路径越多越好(因为这样我们可以把重复的边安排为权值较小的边)
- 我们考虑两种情况:(总共出现的情况也就这两种:路径有、无重叠)
- 如果 a – b – c ,这个三个节点在同一条路径上,这个时候a->b 与 b->c这两个过程是没用重复的路径的,那么这个时候我们将a->b、b-c这个两个过程的最短路的值相加设为k,那么答案就是 将前k小的权值加起来
- 第二种情况,我们考虑两条路径之间有重叠的部分(说明a->b、b->c 这两个过程都经过了相同的某个节点设为x),那么这个时候的过程可以看为:a—>x—>b—>x—>c,这个时候由于b—>x被重复的经过所以我们给b—x上的边分配最小的权值,,,这个时候我们还用考虑 x选择哪个节点好?,这个解决方案就是从1-n枚举x 的值;我们特殊考虑 x == b 是这个时候的路线变成的a—>b—>c 这个路成变成了:第一种情况时的路线了,说明第二种情况是包含第一种的情况的
- 因为第二种情况包含第一种情况,所以我们只需要考虑第二种情况,我们设:
d
a
[
x
]
,
d
b
[
x
]
,
d
c
[
x
]
da[x],db[x],dc[x]
da[x],db[x],dc[x],分别为
a
,
b
,
c
a,b,c
a,b,c到节点x的最短路,我们设
p
r
e
f
[
i
]
pref[i]
pref[i]前i小的边权的前缀和,,,第二种情况的过程为: a—>x—>b—>x—>c,我们可将其拆分成四段路分为两组:
b-->x
、a--x,b--x,x--c
,对于第一组由于b-->x
重复走了两次我们分配给的这里面的边的权值尽可能的小 为: p r e f [ d b [ x ] ] pref[db[x]] pref[db[x]];对与第二组中的边,b--x
中的边在第一组已经被分配好权值,对于接下a-->x,x-->c
来的边我们只需要在所有没有分配的权值中选择较小的给 分配上权值就行了 :第二组的边权和为: p r e f [ d a [ x ] + d b [ x ] + d c [ x ] ] pref[da[x]+db[x]+dc[x]] pref[da[x]+db[x]+dc[x]],最终答案就是两组 边权和相加
代码
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int INF = 0x3f3f3f3f;
vector<vector<int> > g;
void bfs(int s, vector<int> &dis)
{
dis[s] = 0;
queue<int> q;
q.push(s);
while(! q.empty())
{
int u = q.front();
q.pop();
for(auto v : g[u])
{
if(dis[v] == INF)
{
dis[v] = dis[u] + 1;
q.push(v);
}
}
}
}
int main() {
/* freopen("A.txt", "r", stdin); */
/* freopen("Ans.txt", "w", stdout); */
int t;
scanf("%d", &t);
while(t --)
{
int n, m, a, b, c;
scanf("%d %d %d %d %d", &n, &m, &a, &b, &c);
a --, b --, c --;
vector<int> p(m);
for(auto &x : p)
scanf("%d", &x);
sort(p.begin(), p.end());
vector<long long> pref(m+1);
for(int i = 0; i < m; i ++)
pref[i+1] = pref[i] + p[i];
g = vector<vector<int> > (n);
int u, v;
for(int i = 0; i < m; i ++)
{
scanf("%d %d", &u, &v);
u --, v --;
g[u].push_back(v);
g[v].push_back(u);
}
vector<int> da(n, INF), db(n, INF), dc(n, INF);
bfs(a, da);
bfs(b, db);
bfs(c, dc);
long long ans = 1e18;
for(int i = 0; i < n; i ++)
{
if(da[i] + db[i] + dc[i] > m) continue;
ans = min(ans, pref[db[i]] + pref[db[i] + da[i] + dc[i]]);
}
printf("%lld\n", ans);
}
return 0;
}