HDU 3371 Connect the Cities

传送门

这道题各种奇技淫巧,然而也只是勉强不超时。
题目中的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个点而且貌似有重边?(题没说) 那妥妥的用邻接矩阵了,处理重边(选最小的)简单暴力,连通城市处理也是简单暴力。
至于prim0边采取放射状赋和链式赋有区别吗?不知道,懒得试了。这得看数据,然而你又看不到数据。

#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
posted @ 2017-04-08 17:40  CrossingOver  阅读(132)  评论(0编辑  收藏  举报