HDU 3371 Connect the Cities

传送门

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个点而且貌似有重边?(题没说) 那妥妥的用邻接矩阵了,处理重边(选最小的)简单暴力,连通城市处理也是简单暴力。
至于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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值