最小生成树的经典题目.
http://acm.hust.edu.cn/vjudge/contest/view.action?cid=33862#problem/H
首先容易知道我们必须从小到大枚举航空公司,然后对该航空公司占有的边直接加入到生成树中,然后为了保证该生成树的边的权值总和最小,因此剩余的边只能从最小生成树的边中取,因此我们在预处理的时候需要求解一次最小生成树,保存最小生成树上的边的序号,最后我们从最小生成树中选边便只需要O(n)的复杂度.(PS:从最小生成树中一定包含了剩余的需要添加入新的生成树中的边,最小生成树的所有的边和已经加入的边这样的图是存在环的,从环上删除最小生成树的边总能得到我们要构造的生成树),最后我们便采用上述方法遍历所有的航空公司,取最优值,保存相应的信息即可...
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN = 2020;
const int MAXM = 200010;
const int INF = 0x3f3f3f3f;
struct Edge
{
int id, u, v, w, next;
Edge() {}
Edge(int t_id, int t_u, int t_v, int t_w, int t_next) : id(t_id), u(t_u), v(t_v), w(t_w), next(t_next) {}
friend bool operator < (const Edge &e1, const Edge &e2)
{
return e1.w < e2.w;
}
}edge1[MAXM], edge2[MAXM];
int head[MAXN], len, cnt;
int parent[MAXN], ans[MAXN];
int result[MAXN], tmp[MAXN];
int n, m, k, res, res_id, res_cnt;
void Init()
{
len = 0;
memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w, int x, int id)
{
edge2[len].u = u;
edge2[len].v = v;
edge2[len].w = w;
edge2[len].id = id;
edge2[len].next = head[x];
head[x] = len++;
}
void init_set()
{
memset(parent, -1, sizeof(parent));
}
int find_set(int x)
{
return parent[x] < 0 ? x : parent[x] = find_set(parent[x]);
}
void union_set(int x, int y)
{
int r1 = find_set(x), r2 = find_set(y);
if(r1 != r2)
{
if(parent[r2] < parent[r1])
{
parent[r2] += parent[r1];
parent[r1] = r2;
}
else
{
parent[r1] += parent[r2];
parent[r2] = r1;
}
}
return ;
}
void Kruskal()
{
init_set();
int num = 0; //int sum = 0;
sort(edge1 + 1, edge1 + k + 1);
for(int i = 1; i <= k; ++i)
{
int r1 = find_set(edge1[i].u), r2 = find_set(edge1[i].v);
if(r1 != r2)
{
//sum += edge1[i].w;
ans[++num] = i; //注意这里的标号
union_set(edge1[i].u, edge1[i].v);
}
if(num >= n-1) break;
}
//printf("Test: %d\n", sum);
/*
for(int i = 1; i < n; ++i)
{
printf("%d ", ans[i]);
}
printf("\n");
*/
return ;
}
void solve()
{
res = INF, res_id = -1; res_cnt = 0;
int sum, cnt;
for(int i = 1; i <= m; ++i)
{
init_set();
for(int j = head[i]; j != -1; j = edge2[j].next)
{
int u = edge2[j].u, v = edge2[j].v;
if(find_set(u) != find_set(v))
union_set(u, v);
}
sum = 0; cnt = 0;
for(int j = 1; j < n; ++j)
{
int u = edge1[ans[j]].u, v = edge1[ans[j]].v;
//printf("%d %d %d\n", u, v, edge1[ans[j]].w);
if(find_set(u) != find_set(v))
{
sum += edge1[ans[j]].w;
union_set(u, v);
tmp[cnt++] = ans[j];
}
}
if(res > sum)
{
res = sum; res_id = i; res_cnt = cnt;
for(int j = 0; j < cnt; ++j)
{
result[j] = tmp[j];
}
}
}
printf("%d %d %d\n", res, res_id, res_cnt);
sort(result, result + res_cnt);
for(int i = 0; i < res_cnt; ++i)
{
if(i == 0)
printf("%d", edge1[result[i]].id);
else
printf(" %d", edge1[result[i]].id);
}
printf("\n");
return ;
}
int main()
{
//freopen("aa.in", "r", stdin);
int a, b, c, p;
scanf("%d %d %d", &n, &m, &k);
Init();
for(int i = 1; i <= k; ++i)
{
scanf("%d %d %d %d", &a, &b, &c, &p);
edge1[i].u = a, edge1[i].v = b, edge1[i].w = p, edge1[i].id = i;
addEdge(a, b, p, c, i);
}
Kruskal();
solve();
return 0;
}