机试训练2 —— 图论(1)

一、最短路

1. poj 2387 Til the Cows Come Home

    题意:求从n号结点到1号结点的最短路

    思路:spfa最短路

    注意:由于此题中路径是双向的,因此声明数组时,和边有关的数组大小应为边数数据范围的两倍。该题因为数组开小了导致一直runtime error。下给出两种实现spfa中队列的方式,一个用stl中的queue,一个直接手写队列。

//使用stl中的queue实现队列
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdlib>
#include <algorithm>

using namespace std;

int to[4005], cost[4005], head[1005], nnext[4005], dis[1005];
int vis[1005];
int pt = 0;

void add(int a, int b, int s)
{
    to[++ pt] = b;
    cost[pt] = s;
    nnext[pt] = head[a];
    head[a] = pt;
}

void spfa(int n)
{
    queue <int> q;
    for(int i = 1; i <= n; ++ i)
        dis[i] = 0x3f3f3f3f;
    dis[n] = 0;  vis[n] = 1;
    q.push(n);
    while(q.empty() == 0)
    {
        int p = q.front();
        q.pop();
        vis[p] = 0;
        for(int i = head[p]; i != 0; i = nnext[i])
        {
            if(dis[p] + cost[i] < dis[to[i]])
            {
                dis[to[i]] = dis[p] + cost[i];
                if(vis[to[i]] == 0)
                {
                    vis[to[i]] = 1;
                    q.push(to[i]);
                }
            }
        }
    }
}

int main(void)
{
    int t, n;
    int a, b, s;
    scanf("%d%d", &t, &n);
    for(int i = 1; i <= t; ++ i)
    {
        scanf("%d%d%d", &a, &b, &s);
        add(a, b, s);
        add(b, a, s);
    }
    spfa(n);
    printf("%d", dis[1]);
    return 0;
}
//手写队列实现
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdlib>
#include <algorithm>

using namespace std;

int to[4005], cost[4005], he[1005], nnext[4005], dis[1005];
int vis[1005], q[3000000];
int pt = 0;

void add(int a, int b, int s)
{
    to[++ pt] = b;
    cost[pt] = s;
    nnext[pt] = he[a];
    he[a] = pt;
}

void spfa(int n)
{
    for(int i = 1; i <= n; ++ i)
        dis[i] = 0x3f3f3f3f;
    dis[n] = 0;  vis[n] = 1;
    q[1] = n;
    for(int l = 0, r = 1; l < r; )
    {
        int p = q[++ l];
        vis[p] = 0;
        for(int i = he[p]; i != 0; i = nnext[i])
        {
            if(dis[p] + cost[i] < dis[to[i]])
            {
                dis[to[i]] = dis[p] + cost[i];
                if(vis[to[i]] == 0)
                {
                    vis[to[i]] = 1;
                    q[++ r] = to[i];
                }
            }
        }
    }
}

int main(void)
{
    int t, n;
    int a, b, s;
    scanf("%d%d", &t, &n);
    for(int i = 1; i <= t; ++ i)
    {
        scanf("%d%d%d", &a, &b, &s);
        add(a, b, s);
        add(b, a, s);
    }
    spfa(n);
    printf("%d", dis[1]);
    return 0;
}

2. hdu 2112 HDU Today

    题意:求给定起点终点之间的最短路

    思路:对输入地名进行处理,spfa最短路即可

    注意:此题坑较多。(1)起点和终点可能相同,此时结果应为0 (2)有可能输入0条路,如果起终点相同则为0,否则为1 (3)该题中的车的路线是无向图,因此还是注意数组开的大小

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdlib>
#include <algorithm>

using namespace std;

char d[155][35];

int to[20005], cost[20005], he[155], nnext[20005], dis[155];
int vis[155], q[3000000];
int nd = 0, pt = 0;

void add(int a, int b, int s)
{
    to[++ pt] = b;
    cost[pt] = s;
    nnext[pt] = he[a];
    he[a] = pt;
}

void spfa(int n)
{
    for(int i = 1; i <= n; ++ i)
        dis[i] = 0x3f3f3f3f;
    dis[1] = 0;  vis[1] = 1;
    q[1] = 1;
    for(int l = 0, r = 1; l < r; )
    {
        int p = q[++ l];
        vis[p] = 0;
        for(int i = he[p]; i != 0; i = nnext[i])
        {
            if(dis[p] + cost[i] < dis[to[i]])
            {
                dis[to[i]] = dis[p] + cost[i];
                if(vis[to[i]] == 0)
                {
                    vis[to[i]] = 1;
                    q[++ r] = to[i];
                }
            }
        }
    }
}

int main(void)
{
    int n, n1, n2, s;
    bool ok1, ok2;
    char d1[35], d2[35];
    while(scanf("%d", &n) != EOF && n != -1)
    {
        pt = 0;  nd = 0;
        int flag = 0;
        memset(vis, 0, sizeof(vis));
        memset(he, 0, sizeof(he));
        memset(nnext, 0, sizeof(nnext));
        scanf("%s%s", d1, d2);
        strcpy(d[1], d1);
        strcpy(d[2], d2);
        if(strcmp(d1, d2) == 0)
            flag = 1;
        nd = 2;
        for(int i = 1; i <= n; ++ i)
        {
            scanf("%s %s ", d1, d2);
            scanf("%d", &s);
            if(flag == 1)
                continue;
            ok1 = ok2 = false;
            for(int j = 1; j <= nd; ++ j)
            {
                if(strcmp(d[j], d1) == 0)
                {
                    n1 = j;
                    ok1 = true;
                }
                if(strcmp(d[j], d2) == 0)
                {
                    n2 = j;
                    ok2 = true;
                }
            }
            if(ok1 == false)
            {
                nd ++;
                strcpy(d[nd], d1);
                n1 = nd;
            }
            if(ok2 == false)
            {
                nd ++;
                strcpy(d[nd], d2);
                n2 = nd;
            }
            add(n1, n2, s);
            add(n2, n1, s);
        }
        if(flag == 1)
        {
            printf("0\n");
            continue;
        }
        else if(n == 0)
        {
            printf("-1\n");
            continue;
        }
        spfa(nd);
        if(dis[2] == 0x3f3f3f3f)
            printf("-1\n");
        else
            printf("%d\n", dis[2]);
    }
    return 0;
}

3.hdu 1596  find the saftest road

    题意:每条路有自己的安全系数,在0-1之间,包括0和1,整个路径的安全系数是途径每条路安全系数的乘积。求给定起点终点之间安全系数最大的路径的安全系数。

    思路:类似最短路问题。将spfa中间松弛的那步改为 if(dis[i] * cost[i][j] > dis[j]) dis[j] = dis[i] * cost[i][j]即可。求解时,由于求多对路径的最大安全系数。对于每一对,如果从该起点出发的最大系数没有算过,则调用spfa计算从该起点出发到所有结点的最大系数,如果计算过,则直接取计算的值就可以。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdlib>
#include <algorithm>

using namespace std;

double cost[1005][1005], sf[1005][1005];
int vis[1005], cal[1005];
int q[3000000];

void spfa(int st, int n)
{
    memset(vis, 0, sizeof(vis));
    memset(sf[st], 0, sizeof(sf[st]));
    int en = (st + 1) % n + 1;
    sf[st][st] = 1;  vis[st] = 1;
    q[1] = st;
    for(int l = 0, r = 1; l < r; )
    {
        int p = q[++ l];
        vis[p] = 0;
        for(int i = 1; i <= n; ++ i)
        {
            if(i == p)  continue;
            if(sf[st][p] * cost[p][i] > sf[st][i])
            {
                sf[st][i] = sf[st][p] * cost[p][i];
                if(vis[i] == 0)
                {
                    vis[i] = 1;
                    q[++ r] = i;
                }
            }
        }
    }
}

int main(void)
{
    int n, q, st, en;
    while(scanf("%d", &n) != EOF)
    {
        for(int i = 1; i <= n; ++ i)
            for(int j = 1; j <= n; ++ j)
                scanf("%lf", &cost[i][j]);
        scanf("%d", &q);
        for(int i = 1; i <= q; ++ i)
        {
            scanf("%d%d", &st, &en);
            if(en == st)
            {
                printf("1.000\n");
                continue;
            }
            if(cal[st] == 0)
            {
                spfa(st, n);
                cal[st] = 1;
            }
            if(sf[st][en] > 0)
                printf("%.3f\n", sf[st][en]);
            else
                printf("What a pity!\n");
        }
    }
    return 0;
}


4. hdu 2680  choose the best route

    题意:给定终点以及可能的若干个起点,求所有允许起点到终点的最短路中的最小值。

    思路:如果以每个可能的起点算spfa会超时,因此反向建图,以终点为spfa算法的起点,然后比较起到允许起点的时间即可。

    注意:这题有一个问题,使用邻接链表存储边用spfa算法超时,考虑可能是重边的影响,邻接表不好处理重边。由于顶点数量在1000以内,因此可以使用邻接矩阵存储边,此时在读入cost值时直接对重边进行处理,再使用spfa算法不超时。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdlib>
#include <algorithm>

using namespace std;

int dis[1005], vis[1005], mp[1005][1005];
int q[3000000], pt = 0;

void spfa(int st, int n)
{
    for(int i = 1; i <= n; ++ i)
        dis[i] = 0x3f3f3f3f;
    dis[st] = 0;  vis[st] = 1;
    q[1] = st;
    for(int l = 0, r = 1; l < r; )
    {
        int p = q[++ l];
        vis[p] = 0;
        for(int i = 1; i <= n; ++ i)
        {
            if(dis[p] + mp[p][i] < dis[i])
            {
                dis[i] = dis[p] + mp[p][i];
                if(vis[i] == 0)
                {
                    vis[i] = 1;
                    q[++ r] = i;
                }
            }
        }
    }
}

void init(int n)
{
    for(int i = 1; i <= n; ++ i)
    {
        for(int j = 1; j <= n; ++ j)
        {
            if(i == j) mp[i][j] = 0;
            else       mp[i][j] = 0x3f3f3f3f;
        }
    }
}

int main(void)
{
    int n, m, s, w;
    int a, b, t, tp;
    while(scanf("%d%d%d", &n, &m, &s) != EOF)
    {
        memset(vis, 0, sizeof(vis));
        memset(head, 0, sizeof(head));
        memset(nxt, 0, sizeof(nxt));
        init(n);
        for(int i = 1; i <= m; ++ i)
        {
            scanf("%d%d%d", &a, &b, &t);
            if(t < mp[b][a])
                mp[b][a] = t;
        }
        spfa(s, n);
        scanf("%d", &w);
        int res = 0x3f3f3f3f;
        for(int i = 1; i <= w; ++ i)
        {
            scanf("%d", &tp);
            if(dis[tp] < res)
                res = dis[tp];
        }
        if(res == 0x3f3f3f3f)
            printf("-1\n");
        else
            printf("%d\n", res);
    }
    return 0;
}

5.hdu 1690  Bus System

    题意:在x坐标上有若干车站,两个车站之间的票价由一个分段函数决定,距离越远,票价越高。求给定两车站之的最小值。

    思路:此题先根据距离将任意两个车站之间的票价得到,再使用spfa即可。

    注意:该题需要注意一下几点,首先结果可能很大,应该用long long存最终结果。由于使用long long 存结果,因此无穷大也必须是使用64为整数的形式,这里用的是0x3f3f3f3f3f3f3f3f。再就是注意输出格式,此题因为输出时少了一个句点wrong answer了好几次。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdlib>
#include <algorithm>

using namespace std;

int vis[105], flag[105], pos[105];
int q[3000000], pt = 0;
int l1, l2, l3, l4, c1, c2, c3, c4;
long long dis[105][105], mp[105][105];

void spfa(int st, int n)
{
    memset(vis, 0, sizeof(vis));
    for(int i = 1; i <= n; ++ i)
        dis[st][i] = 0x3f3f3f3f3f3f3f3f;
    dis[st][st] = 0;  vis[st] = 1;
    q[1] = st;
    for(int l = 0, r = 1; l < r; )
    {
        int p = q[++ l];
        vis[p] = 0;
        for(int i = 1; i <= n; ++ i)
        {
            if(i == p)  continue;
            if(dis[st][p] + mp[p][i] < dis[st][i])
            {
                dis[st][i] = dis[st][p] + mp[p][i];
                if(vis[i] == 0)
                {
                    vis[i] = 1;
                    q[++ r] = i;
                }
            }
        }
    }
}

long long get_cost(int x)
{
    if(x > 0 && x <= l1)   return c1;
    if(x > l1 && x <= l2)  return c2;
    if(x > l2 && x <= l3)  return c3;
    if(x > l3 && x <= l4)  return c4;
    if(x > l4)             return 0x3f3f3f3f3f3f3f3f;
}

void get_edge(int n)
{
    for(int i = 1; i <= n; ++ i)  mp[i][i] = 0;
    for(int i = 1; i <= n; ++ i)
    {
        for(int j = i + 1; j <= n; ++ j)
        {
            mp[i][j] = get_cost(fabs(pos[j] - pos[i]));
            mp[j][i] = mp[i][j];
        }
    }
}

int main(void)
{
    int t, n, m, s, e;
    scanf("%d", &t);
    for(int t0 = 1; t0 <= t; ++ t0)
    {
        printf("Case %d:\n", t0);
        memset(flag, 0, sizeof(flag));
        scanf("%d%d%d%d", &l1, &l2, &l3, &l4);
        scanf("%d%d%d%d", &c1, &c2, &c3, &c4);
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++ i)
            scanf("%d", &pos[i]);
        get_edge(n);
        for(int i = 1; i <= m; ++ i)
        {
            scanf("%d%d", &s, &e);
            if(flag[s] == 0)
            {
                spfa(s, n);
                flag[s] = 1;
            }
            if(dis[s][e] == 0x3f3f3f3f3f3f3f3f)
                printf("Station %d and station %d are not attainable.\n", s, e);
            else
                printf("The minimum cost between station %d and station %d is %lld.\n", s, e, dis[s][e]);
        }
    }
    return 0;
}

6.hdu 2433  Travel

    题意:给出图中的所有双向路,路长度为1,求去掉已有路中的每一条后,任两个城镇之间的最短路的和是多少。

    思路:该题容易想到,针对每条删掉的路,对每一个顶点都重新进行一次最短路的计算然后相加,但直接采用这种方式会超时。当删掉某条路时,某些最短路是不会改变的。首先,计算出完整图中从每个点i出发的最短路树的和sum[i],考虑使用数组used[i][a][b]表示在i的最短路树中是否使用了边ab,如果未使用边ab,那么删掉ab边时就不需要重新计算i的最短路树了。

    注意:该题重边的判定也是可以减少计算时间的关键。如果used[i][a][b]是true并且ab间只有一条边的时候,才重新计算i的最短路树,如果ab之间有多条边,那也不需在重新计算了。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdlib>
#include <algorithm>

using namespace std;

int q[3000000], pt = 0;
int dis[105], mp[105][105], vis[105], edge[3005][2], sum[105], pre[105], tmp[105][105];
bool used[105][105][105];

void spfa(int st, int n, int choice)
{
    memset(vis, 0, sizeof(vis));
    for(int i = 1; i <= n; ++ i)
        dis[i] = 0x3f3f3f3f;
    for(int i = 1; i <= n; ++ i)
        pre[i] = i;
    dis[st] = 0;  vis[st] = 1;
    q[1] = st;
    for(int l = 0, r = 1; l < r; )
    {
        int p = q[++ l];
        vis[p] = 0;
        for(int i = 1; i <= n; ++ i)
        {
            if(i == p)  continue;

            int tp;
            if(choice == 1)
            {
                if(mp[p][i] > 1 && mp[p][i] < 0x3f3f3f3f) tp = 1;
                else tp = mp[p][i];
            }
            else if(choice == 2)
            {
                if(tmp[p][i] > 1 && tmp[p][i] < 0x3f3f3f3f) tp = 1;
                else tp = tmp[p][i];
            }

            if(dis[p] + tp < dis[i])
            {
                dis[i] = dis[p] + tp;
                pre[i] = p;
                if(vis[i] == 0)
                {
                    vis[i] = 1;
                    q[++ r] = i;
                }
            }
        }
    }
}

void init(int n)
{
    for(int i = 1; i <= n; ++ i)
    {
        for(int j = 1; j <= n; ++ j)
        {
            if(i == j)  mp[i][j] = 0;
            mp[i][j] = 0x3f3f3f3f;
        }
    }
}

void init1(int n)
{
    for(int i = 1; i <= n; ++ i)
    {
        for(int j = 1; j <= n; ++ j)
        {
            if(i == j)  tmp[i][j] = 0;
            tmp[i][j] = 0x3f3f3f3f;
        }
    }
}

int main(void)
{
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF)
    {
        init(n);
        for(int i = 1; i <= m; ++ i)
        {
            scanf("%d%d", &edge[i][0], &edge[i][1]);
            int s = edge[i][0], e = edge[i][1];
            if(mp[s][e] == 0x3f3f3f3f)  mp[s][e] = 1;
            else if(mp[s][e] >= 1) mp[s][e] += 1;
            else mp[s][e] = 0;
            mp[e][s] = mp[s][e];
        }
        memset(sum, 0, sizeof(sum));
        memset(used, 0, sizeof(used));
        for(int i = 1; i <= n; ++ i)
        {
            spfa(i, n, 1);
            for(int j = 1; j <= n; ++ j)
            {
                if(i == j)  continue;
                sum[i] += dis[j];
                used[i][pre[j]][j] = true;
                used[i][j][pre[j]] = true;
            }
        }

        for(int i = 1; i <= m; ++ i)
        {
            int res = 0;
            for(int j = 1; j <= n; ++ j)
            {
                int s = edge[i][0], e = edge[i][1];
                if(used[j][s][e] == true && mp[s][e] == 1)
                {
                    init1(n);
                    for(int i0 = 1; i0 <= m; ++ i0)
                    {
                        if(i0 == i)  continue;
                        int s = edge[i0][0], e = edge[i0][1];
                        if(tmp[s][e] == 0x3f3f3f3f)  tmp[s][e] = 1;
                        else if(tmp[s][e] >= 1) tmp[s][e] += 1;
                        else tmp[s][e] = 0;
                        tmp[e][s] = tmp[s][e];
                    }
                    spfa(j, n, 2);
                    for(int k = 1; k <= n; ++ k)
                    {
                        if(k == j)  continue;
                        res += dis[k];
                        if(dis[k] == 0x3f3f3f3f)
                        {
                            res = 0x3f3f3f3f;
                            break;
                        }
                    }
                }
                else
                    res += sum[j];
                if(res == 0x3f3f3f3f)
                    break;
            }
            if(res == 0x3f3f3f3f)
                printf("INF\n");
            else
                printf("%d\n", res);
        }
    }
    return 0;
}

二、最小生成树

1.hdu 1863 畅通工程

    题意:给定村庄之间的路,求将所有村庄联系起来的最小成本

    思路:这是一个最小生成树模板题,直接用kruskal求解即可。

    注意:该题最后需要判断是否存在最小生成树。判断的正确方法是在kruskal后记录pre[i] = i的点的数量,如果大于1个,那么不存在最小生成树。最开始用的方法是判断所有点的pre是否都相等,这个是有问题的,如果kruskal算法运行时,5个结点最后一条边合并之前分组是124和35,且35中父节点是3,最后3、4结点合并,合并时并不会改变5的pre,合并结束以后5的pre还是3,但这个时候所有结点都在一个集合中。所以应该通过pre[i]=i的数量来判断。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <vector>
#include <algorithm>

using namespace std;

struct edge
{
    int s, e, cost;
};

int cmp(edge a, edge b)
{
    return a.cost < b.cost;
}

edge g[10005];
int pre[105], rnk[105];

void init(int m)
{
    for(int i = 1; i <= m; ++ i)
    {
        pre[i] = i;
        rnk[i] = 0;
    }
}

int find_rt(int x)
{
    if(x == pre[x])
        return x;
    return pre[x] = find_rt(pre[x]);
}

int union_rnk(int x, int y)
{
    int px = find_rt(x);
    int py = find_rt(y);
    if(px == py)
        return 0;
    if(rnk[px] < rnk[py])
        pre[px] = py;
    else
    {
        pre[py] = px;
        if(rnk[px] == rnk[py])
            rnk[px] ++;
    }
    return 1;
}

int kruskal(int n, int m)
{
    int res = 0;
    sort(g, g + n, cmp);
    init(m);
    for(int i = 0; i < n; ++ i)
    {
        int tmp = union_rnk(g[i].s, g[i].e);
        res += tmp * g[i].cost;
    }
    return res;
}

int main(void)
{
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF && n != 0)
    {
        for(int i = 0; i < n; ++ i)
            scanf("%d%d%d", &g[i].s, &g[i].e, &g[i].cost);
        int res = kruskal(n, m);
        int cnt = 0;
        for(int i = 1; i <= m; ++ i)
        {
            if(pre[i] == i)
                cnt ++;
        }
        if(cnt > 1)
            printf("?\n");
        else
            printf("%d\n", res);
    }
    return 0;
}

    该题也可使用prim算法,在prim中用res记录最小值,在判断中只要发现不连通的两部分,就马上退出算法。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <vector>
#include <algorithm>

using namespace std;

int cost[105][105], vis[105], dis[105];

int prim(int m)
{
    int res = 0;
    for(int i = 1; i <= m; ++ i)
        dis[i] = cost[1][i];
    dis[1] = 0;  vis[1] = 1;
    for(int i = 2; i <= m; ++ i)
    {
        int mins = 0x3f3f3f3f, x = 0;
        for(int j = 1; j <= m; ++ j)
        {
            if(vis[j] == 0 && dis[j] < mins)
            {
                mins = dis[j];
                x = j;
            }
        }
        if(x == 0)  return 0x3f3f3f3f;
        res += dis[x];
        vis[x] = 1;
        for(int j = 1; j <= m; ++ j)
        {
            if(vis[j] == 0 && cost[x][j] < dis[j])
                dis[j] = cost[x][j];
        }
    }
    return res;
}

int main(void)
{
    int n, m, s, e, t;
    while(scanf("%d%d", &n, &m) != EOF && n != 0)
    {
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= m; ++ i)
        {
            for(int j = 1; j <= m; ++ j)
            {
                if(i == j)  cost[i][j] = 0;
                else  cost[i][j] = 0x3f3f3f3f;
            }
        }
        for(int i = 0; i < n; ++ i)
        {
            scanf("%d%d%d", &s, &e, &t);
            cost[s][e] = t;
            cost[e][s] = t;
        }
        int res = prim(m);

        if(res == 0x3f3f3f3f)
            printf("?\n");
        else
            printf("%d\n", res);
    }
    return 0;
}

2.hdu 3371 connect the cities

    题意:有某些城市之间有连接,现修路使所有城市均连在一起。

    思路:将已连接的城市先用并查集处理一下,然后再用kruskal求最小生成树即可。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <queue>
#include <vector>
#include <algorithm>

using namespace std;

struct edge
{
    int s, e, cost;
};

edge g[25005];
int pre[505], rnk[505];

int cmp(edge a, edge b)
{
    return a.cost < b.cost;
}

void init(int n)
{
    for(int i = 1; i <= n; ++ i)
    {
        pre[i] = i;
        rnk[i] = 0;
    }
}

int find_rt(int x)
{
    if(pre[x] == x)
        return x;
    return pre[x] = find_rt(pre[x]);
}

int union_rnk(int x, int y)
{
    int px = find_rt(x);
    int py = find_rt(y);
    if(px == py)
        return 0;
    if(rnk[px] < rnk[py])
        pre[px] = py;
    else
    {
        pre[py] = px;;
        if(rnk[px] == rnk[py])
            rnk[px] ++;
    }
    return 1;
}

int kruskal(int n, int m)
{
    int res = 0;
    sort(g, g + m, cmp);
    for(int i = 0; i < m; ++ i)
    {
        int tmp = union_rnk(g[i].s, g[i].e);
        res += tmp * g[i].cost;
    }
    return res;
}

int main(void)
{
    int t, n, m, k, nt, ct1, ct2;
    scanf("%d", &t);
    for(int t0 = 1; t0 <= t; ++ t0)
    {
        scanf("%d%d%d", &n, &m, &k);
        init(n);
        for(int i = 0; i < m; ++ i)
            scanf("%d%d%d", &g[i].s, &g[i].e, &g[i].cost);
        for(int i = 1; i <= k; ++ i)
        {
            scanf("%d", &nt);
            scanf("%d", &ct1);
            for(int j = 2; j <= nt; ++ j)
            {
                scanf("%d", &ct2);
                union_rnk(ct1, ct2);
            }
        }
        int res = kruskal(n, m);
        int cnt = 0;
        for(int i = 1; i <= n; ++ i)
        {
            if(pre[i] == i)
                cnt ++;
        }
        if(cnt > 1)
            printf("-1\n");
        else
            printf("%d\n", res);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_33872397/article/details/82252118