T2:Minimum
【题目描述】
给出一幅由n个点m条边构成的无向带权图。
其中有些点是黑点,另外的是白点。
现在每个白点都要与他距离最近的黑点通过最短路连接(如果有很多个,可以选取其中任意一个),我们想要使得花费的代价最小,请问这个最小代价是多少。
注意:最后选出的边保证每个白点到黑点的距离仍然是最短距离。
【输入格式】
第一行两个整数 n,m ;
第二行 n 个整数,0 表示白点,1 表示黑点;
接下来 m 行,每行三个整数 x,y,z ,表示一条连接 x 和 y 点,权值为 z 的边。
【输出格式】
如果无解,输出“impossible”,否则,输出最小代价。
【输入样例】
5 7
0 1 0 1 0
1 2 11
1 3 1
1 5 17
2 3 1
3 5 18
4 5 3
2 4 5
【输出样例】
5 (选2、4、6)
【数据范围】
对 30% 的输入数据 :1≤n≤10,1≤m≤20;
对 100% 的输入数据 :1≤n≤100000,1≤m≤200000,1≤z≤1000000000 。
首先还是30%的数据:枚举每条边的选取情况,暴力判断;
然后就是100%的数据:我们添加一个超级点S,在S与每个黑点之间连一条权值为0的边,先处理从S出发到每个点的最短距离,我们可以取出一张最短路径图,现在问题就变成了取权值最小的边的集合,使得这幅图连接,那么,最小生成树的算法就能AC此题。
贴代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN=1e6+10;
const ll MAXM=2e6+10;
const ll INF=0x3f3f3f3f;
ll Read()
{
ll i=0,f=1;
char c;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
f=-1,c=getchar();
for(;c>='0'&&c<='9';c=getchar())
i=(i<<1)+(i<<3)+c-'0';
return i*f;
}
ll n,m,cnt;
ll dis[MAXN],head[MAXN],nxt[MAXM];
ll fa[MAXN];
priority_queue<pair<int,int> > q;
struct edge{
int from,to,w;
}e[MAXM],t[MAXM];
void add(int a,int b,int c)
{
cnt++;
nxt[cnt]=head[a];
head[a]=cnt;
e[cnt].from=a;
e[cnt].to=b;
e[cnt].w=c;
}
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
int find(int x)
{
if(fa[x]!=x)
fa[x]=find(fa[x]);
return fa[x];
}
void dijkstra(ll st)
{
memset(dis,INF,sizeof(dis));
dis[st]=0;
q.push(make_pair(0,st));
while(!q.empty())
{
int u=q.top().second;
q.pop();
for(int i=head[u];i!=-1;i=nxt[i])
{
int v=e[i].to;
if(dis[v]>dis[e[i].from]+e[i].w)
{
dis[v]=dis[e[i].from]+e[i].w;
q.push(make_pair(-dis[v],v));
}
}
}
}
int main()
{
memset(head,-1,sizeof(head));
memset(nxt,-1,sizeof(nxt));
n=Read(),m=Read();
for(ll i=1;i<=n;++i)
{
int col=Read();
if(col)
{
add(i,0,0);
add(0,i,0);
}
}
for(ll i=1;i<=m;++i)
{
ll x,y,z;
x=Read(),y=Read(),z=Read();
add(x,y,z);
add(y,x,z);
}
dijkstra(0);
ll tcnt=cnt;
cnt=0;
for(int i=1;i<=tcnt;++i)
{
if(dis[e[i].from]+e[i].w==dis[e[i].to]||dis[e[i].from]==dis[e[i].to]+e[i].w)
{
t[++cnt].from=e[i].from;
t[cnt].to=e[i].to;
t[cnt].w=e[i].w;
}
}
sort(t+1,t+cnt+1,cmp);
for(ll i=1;i<=n;++i)
fa[i]=i;
ll ans=0,suc=0;
for(ll i=1;i<=cnt;++i)
{
ll a=find(t[i].from),b=find(t[i].to);
if(a!=b)
{
ans+=t[i].w;
suc++;
fa[a]=b;
}
}
if(suc!=n)
{
cout<<"impossible";
}
else
cout<<ans;
return 0;
}