题目链接:Dima and Bacteria
题意:
给出n个点,m条边,每条边是双向的,并且有权值。n个点分为k个种类,每个种类有Ci个点
如果任意两个相同点之间转移,可以找到一条路径,该路径满足经过的边的权值和为0则输出Yes,输出任意两个种类的点之间转移的最小路径。否则输出No;
思路:
分为两个问题
1.先用并查集判断是否相同种类任意两点是连通的。
对权值为0的边的顶点unite,然后查找每个种类的点是否都连通。
2.最短路问题
点不多,用floyd算法,解决最短路。d[i][j]=mi{d[i][j],d[i][k]+d[k][j]}
遇到的问题:
1.刚开始没想到可以用并查集解决第一个问题。
2.初始化越界了,找了好久。。。
3.floyd算法模板打错了。
4.并查集维护,应该对所有花费为0 的点都进行维护,刚开始就只对相同种类的点进行 维护。后来才发现错了。
第一次d题没去看题解解决了。但是写题半小时debug两小时。写题的时候细节没处理好,思维不够全面,一些细节总是想当然,一旦钻进牛角尖就很难出来了。
代码:
#include<iostream>
#include<vector>
#include<algorithm>
const int INF = 999999999;
using namespace std;
int c[610], d[510][510], type[101000], n, m, k, pre[101000];
//初始化
void init()
{
int i, j;
c[0] = 0;
for (i = 1, j = 1; i <= k; ++i)
{
for (int l = 0; l<c[i]; ++l, ++j)
{
type[j] = i;
}
}
for (i = 1; i <= k; ++i)
{
for (j = 1; j <=k; ++j)
{
if (i == j)d[i][j] = 0;
else
d[i][j] = INF;
}
}
for (i = 1; i <= n; ++i)
{
pre[i] = i;
}
}
//并查集
int find(int x)
{
if (pre[x] == x)
return x;
else
{
return pre[x] = find(pre[x]);
}
}
void Unite(int x, int y)
{
int fx = find(x), fy = find(y);
if (fx != fy)pre[fy] = fx;
}
int main()
{
int i, j;
cin >> n >> m >> k;
for (i = 1; i <= k; ++i)
cin >> c[i];
init();
for (i = 0; i<m; ++i)
{
int s, t, p;
cin >> s >> t >> p;
if (type[s]!= type[t])
{
d[type[s]][type[t]] = min(p, d[type[s]][type[t]]);
d[type[t]][type[s]] = min(p, d[type[t]][type[s]]);
}
//对边的权值为0 进行维护
if (p == 0) {
Unite(s, t);
}
}
int flag = 0;
//判断是否连通
for (i = 1, j = 1; i <= k; ++i)
{
int f = find(j);
for (int l = 0; l<c[i]; ++l, ++j)
{
if (find(j)!=f) {
flag = 1;
break;
}
}
if (flag)break;
}
if (flag)
{
cout << "No" << endl;
}
else
{
cout << "Yes" << endl;
//floyd算法找最短路
for (i = 1; i <= k; ++i)
{
for (j = 1; j <= k; ++j)
{
for (int l = 1 ; l <= k; ++l)
{
d[j][l] = min(d[j][l], d[j][i] + d[i][l]);
}
}
}
for (i = 1; i <= k; ++i)
{
for (j = 1; j <=k; ++j)
{
if (d[i][j] == INF)cout << -1<<" ";
else
cout <<d[i][j]<<" ";
}
cout << endl;
}
}
return 0;
}