和HDU 1879差不多,但是这道题各种奇技淫巧,然而也只是勉强不超时。
题目中的k
不是指k
个连通的城市,是指k
行信息,每行才是相互连通的城市。(而且看样例可以发现,每行的城市不一定是连通分量)
第一种用并查集做,关于连通的城市怎么处理,不能union
两点(神tm超时),只能往边表里添加一条权值为0
的边,而且要顺着加边(一条链),不能放射状加边。
而且好像不来并查集那两个优化还过不去?(那俩优化表示我终于派上用场了)不知道。反正过去了也是九百多ms,没什么意思。
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int MAXN = 1e2 * 5 + 5;
int N, M, K;
int pre[MAXN];
int conn;
int opt;
struct edge
{
int n1, n2, w;
bool operator<(const edge& e) const
{
return w < e.w;
}
};
vector<edge> v;
int f(int x)
{
int f0 = x, f1 = x;
for (; pre[f0] > 0;)
{
f0 = pre[f0];
}
for (; pre[f1] > 0;)
{
int t = f1;
f1 = pre[f1];
pre[t] = f0;
}
return f0;
}
bool u(int a, int b)
{
int f1 = f(a);
int f2 = f(b);
if (f1 != f2)
{
conn++;
if (pre[f1] <= pre[f2])
{
pre[f1] += pre[f2];
pre[f2] = f1;
}
else
{
pre[f2] += pre[f1];
pre[f1] = f2;
}
return true;
}
return false;
}
int main()
{
int T;
int a, b, c;
scanf("%d", &T);
for (; T--;)
{
scanf("%d%d%d", &N, &M, &K);
memset(pre, -1, sizeof pre);
v.clear();
conn = 0;
opt = 0;
for (int i = 0; i < M; i++)
{
scanf("%d%d%d", &a, &b, &c);
v.push_back({ a,b,c });
}
for (int i = 0; i < K; i++)
{
scanf("%d%d", &a, &b);
//int y = f(b);
for (int j = 1; j < a; j++)
{
scanf("%d", &c);
//u(y, c); 如 4 1 2 3 4
v.push_back({ b,c,0 }); 神tm 只能按照每行所给的连通的顶点顺序,加权值为0的边1-2 2-3 3-4,不能 union,也不能加1-2 1-3 1-4
b = c;
}
}
sort(v.begin(), v.end());
for (int i = 0; i < v.size(); i++)
{
if (conn == N - 1) break;
if (u(v[i].n1, v[i].n2))
opt += v[i].w;
}
if (conn != N - 1) printf("-1\n");
else printf("%d\n", opt);
}
return 0;
}
第二种prim
,500个点而且貌似有重边?(题没说) 那妥妥的用邻接矩阵了,处理重边(选最小的)简单暴力,连通城市处理也是简单暴力。
至于prim
赋0
边采取放射状赋和链式赋有区别吗?不知道,懒得试了。这得看数据,然而你又看不到数据。
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int INF = 1e9;
const int MAXN = 1e2 * 5 + 5;
int N, M, K;
int w[MAXN][MAXN];
int d[MAXN];
bool vis[MAXN];
int opt;
int prim(int s)
{
d[s] = 0;
for (int i = 0; i < N; i++)
{
int mind = INF, u = -1;
for (int j = 1; j <= N; j++)
{
if (!vis[j] && d[j] < mind)
{
mind = d[j];
u = j;
}
}
if (u == -1) return -1;
vis[u] = true;
opt += mind;
for (int j = 1; j <= N; j++)
{
if (!vis[j] && w[u][j] != INF)
{
if (w[u][j] < d[j])
{
d[j] = w[u][j];
}
}
}
}
return opt;
}
int main()
{
int T;
int a, b, c;
scanf("%d", &T);
for (; T--;)
{
scanf("%d%d%d", &N, &M, &K);
fill(w[0], w[0] + MAXN*MAXN, INF);
fill(d, d + MAXN, INF);
fill(vis, vis + MAXN, false);
opt = 0;
for (int i = 0; i < M; i++)
{
scanf("%d%d%d", &a, &b, &c);
if (c < w[a][b]) w[a][b] = w[b][a] = c; 重边
}
for (int i = 0; i < K; i++)
{
scanf("%d%d", &a, &b);
for (int j = 1; j < a; j++)
{
scanf("%d", &c); 如 4 1 2 3 4
w[b][c] = w[c][b] = 0; 神tm 只能按照每行所给的连通的顶点顺序,加权值为0的边1-2 2-3 3-4,不能 union,也不能加1-2 1-3 1-4
b = c;
}
}
printf("%d\n", prim(1));
}
return 0;
}
// 889ms hehe