文章目录
HDU1233 还是畅通工程
传送门:HDU1233 还是畅通工程
解题思路
N个点,N * (N - 1) / 2 条边,且 N < 100,邻接矩阵存图,朴素prim即可。
AC代码1【朴素prim + 邻接矩阵】
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int MAXN = 105;
const int INF = 0x3f3f3f3f;
int n, m, len;
int dis[MAXN],vis[MAXN];
int g[MAXN][MAXN];
priority_queue<pii,vector<pii>,greater<pii> > q;
void init()
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
g[i][j] = INF;
}
g[i][i] = 0;
}
}
int prim()
{
int ans = 0;
memset(dis,INF,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1] = 0;
while (true)
{
int v = -1;
for (int i = 1; i <= n; i++)
{
if (!vis[i] && (v == -1 || dis[i] < dis[v]))
{
v = i;
}
}
if (v == -1)
{
break;
}
vis[v] = true;
ans += dis[v];
for (int i = 1; i <= n; i++)
{
dis[i] = min(dis[i],g[v][i]);
}
}
return ans;
}
int main()
{
int a, b, c;
while (scanf("%d",&n) && n)
{
init();
m = (n * (n - 1)) >> 1;
while (m--)
{
scanf("%d%d%d",&a, &b, &c);
g[a][b] = g[b][a] = min(g[a][b], c);
}
printf("%d\n",prim());
}
return 0;
}
AC代码2【堆优化prim + 邻接矩阵】
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int MAXN = 105;
const int INF = 0x3f3f3f3f;
int n, m, len;
int dis[MAXN],vis[MAXN];
int g[MAXN][MAXN];
priority_queue<pii,vector<pii>,greater<pii> > q;
void init()
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
g[i][j] = INF;
}
g[i][i] = 0;
}
}
int prim()
{
int ans = 0;
memset(dis,INF,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1] = 0;
q.push(pii(0,1));
while (!q.empty())
{
pii temp = q.top();
q.pop();
int v = temp.second;
if (vis[v])
{
continue;
}
vis[v] = true;
ans += dis[v];
for (int i = 1; i <= n; i++)
{
if (!vis[i] && dis[i] > g[v][i])
{
dis[i] = g[v][i];
q.push(pii(dis[i],i));
}
}
}
return ans;
}
int main()
{
int a, b, c;
while (scanf("%d",&n) && n)
{
init();
m = (n * (n - 1)) >> 1;
while (m--)
{
scanf("%d%d%d",&a, &b, &c);
g[a][b] = g[b][a] = min(g[a][b], c);
}
printf("%d\n",prim());
}
return 0;
}
AC代码3【堆优化prim+ 邻接表】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int MAXN = 105;
const int INF = 0x3f3f3f3f;
int n, m;
int dis[MAXN],vis[MAXN];
vector<pii> edge[MAXN];
priority_queue<pii,vector<pii>,greater<pii> > q;
int prim()
{
int ans = 0;
memset(dis,INF,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1] = 0;
q.push(pii(0,1));
while (!q.empty())
{
pii t = q.top();
q.pop();
int v = t.second;
if (vis[v])
{
continue;
}
vis[v] = 1;
ans += dis[v];
for (int i = 0; i < edge[v].size(); i++)
{
int p = edge[v][i].first;
int w = edge[v][i].second;
if (dis[p] > w)
{
dis[p] = w;
q.push(pii(dis[p], p));
}
}
}
for (int i = 0; i <= n; i++)
{
edge[i].clear();
}
return ans;
}
int main()
{
int a, b, c;
while (scanf("%d",&n) && n)
{
m = (n * (n - 1)) >> 1;
while (m--)
{
scanf("%d%d%d",&a, &b, &c);
edge[a].push_back(pii(b,c));
edge[b].push_back(pii(a,c));
}
printf("%d\n",prim());
}
return 0;
}
HDU1879 继续畅通工程
传送门:HDU1879 继续畅通工程
解题思路
N个点,N * (N - 1) / 2 条边,且 1 < N < 100,可以用邻接矩阵存图。对于已经修建道路的两座城市,使其距离为0,然后用朴素prim即可。
AC代码【朴素prim + 邻接矩阵】
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int MAXN = 800;
const int INF = 0x3f3f3f3f;
int n, m, len;
int dis[MAXN],vis[MAXN];
int g[MAXN][MAXN];
priority_queue<pii,vector<pii>,greater<pii> > q;
void init()
{
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
g[i][j] = INF;
}
g[i][i] = 0;
}
}
int prim()
{
int ans = 0;
memset(dis,INF,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[1] = 0;
while (true)
{
int v = -1;
for (int i = 1; i <= n; i++)
{
if (!vis[i] && (v == -1 || dis[i] < dis[v]))
{
v = i;
}
}
if (v == -1)
{
break;
}
vis[v] = true;
ans += dis[v];
for (int i = 1; i <= n; i++)
{
dis[i] = min(dis[i],g[v][i]);
}
}
return ans;
}
int main()
{
int a, b, c, d;
while (scanf("%d",&n) && n)
{
init();
m = (n * (n - 1)) >> 1;
while (m--)
{
scanf("%d%d%d%d",&a, &b, &c, &d);
// 城市之间已有直接连通的道路,无需修建
if (d == 1)
{
c = 0;
}
g[a][b] = g[b][a] = min(g[a][b], c);
}
printf("%d\n",prim());
}
return 0;
}
POJ1751 Highways
传送门:POJ1751 Highways
题目大意
给定一个N和N个城市的坐标,然后给定一个M和M对已经相连的城市。问:将要将所有城市连通起来且总路径最少,还需连接哪些城市?
提示:本题有Special judge,即连接城市的输出可以与给定样例不一致,只要正确即可。
解题思路
N个城市,任意两个城市都要求得距离,那就是N * (N - 1) / 2条边,由题意可知1 <= N <= 750,所以对于prim算法可以直接采用邻接矩阵存图。
然后这里并不需要求得总路径长度,所以不用求得具体距离(对两点间距离无需开根)。
AC代码1【朴素prim + 邻接矩阵】
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int MAXN = 800;
const int INF = 0x3f3f3f3f;
int n, m, len;
int dis[MAXN],vis[MAXN],pre[MAXN],x[MAXN],y[MAXN];
int g[MAXN][MAXN];
priority_queue<pii,vector<pii>,greater<pii> > q;
void prim()
{
memset(dis,INF,sizeof(dis));
memset(vis,0,sizeof(vis));
// 用 pre 数组记录某个点是由哪个点中转而来
for (int i = 1; i <= n; i++)
{
pre[i] = 1;
}
dis[1] = 0;
while (true)
{
int v = -1;
for (int i = 1; i <= n; i++)
{
if (!vis[i] && (v == -1 || dis[i] < dis[v]))
{
v = i;
}
}
if (v == -1)
{
break;
}
vis[v] = true;
// 若点 pre[v] 和点 v 不连通
if (g[pre[v]][v])
{
// 这两点间需要建路
printf("%d %d\n",pre[v],v);
}
for (int i = 1; i <= n; i++)
{
if (dis[i] > g[v][i])
{
dis[i] = g[v][i];
// 点 i 由点 v 更新而来
pre[i] = v;
}
}
}
}
int main()
{
int a, b;
scanf("%d",&n);
for (int i = 1; i <= n; i++)
{
scanf("%d%d",&x[i],&y[i]);
for (int j = 1; j < i; j++)
{
// 求距离,因为不需要具体距离,所以无需开根
len = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
g[i][j] = g[j][i] = len;
}
}
scanf("%d",&m);
while (m--)
{
scanf("%d%d",&a, &b);
// 已经修建道路的城市距离为 0
g[a][b] = g[b][a] = 0;
}
prim();
return 0;
}
AC代码2【kruskal】
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int MAXN = 800;
const int INF = 0x3f3f3f3f;
int x[MAXN], y[MAXN], father[MAXN];
int n, m, len, node = 0;
struct Node
{
int a, b, c;
Node(){}
bool operator < (const Node& x) const
{
return c < x.c;
}
Node(int x, int y, int z)
{
a = x;
b = y;
c = z;
}
}edge[MAXN * MAXN];
void init(int maxn)
{
for (int i = 1; i <= maxn; i++)
{
father[i] = i;
}
node = 0;
}
int find(int x)
{
if (father[x] == x)
{
return x;
}
return father[x] = find(father[x]);
}
bool merge(int x, int y)
{
x = find(x);
y = find(y);
if (x != y)
{
father[x] = y;
// 两个点未连接
return true;
}
// 两个点已连接
return false;
}
void kruskal()
{
// 对边排序,便于后续对边贪心
sort(edge,edge + node);
int cnt = 0;
for (int i = 0; i < node; i++)
{
// 每次取出一条边,判断两个点是否已经连接
if (merge(edge[i].a, edge[i].b))
{
printf("%d %d\n",edge[i].a, edge[i].b);
}
// 已经找到 N - 1 条边了
if (cnt == n - 1)
{
break;
}
}
return ;
}
int main()
{
int a, b;
scanf("%d",&n);
init(n);
for (int i = 1; i <= n; i++)
{
scanf("%d%d",&x[i],&y[i]);
for (int j = 1; j < i; j++)
{
// 求距离,因为不需要具体距离,所以无需开根
len = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
edge[node++] = Node(i,j,len);
}
}
scanf("%d",&m);
while (m--)
{
scanf("%d%d",&a, &b);
// 已经修建道路的城市在一个集合中
merge(a, b);
}
kruskal();
return 0;
}
HDU5253 连接的管道
传送门:HDU5253 连接的管道
解题思路
求最小生成树的总路径长度。根据题意可知是稀疏图,所以可以采用kruskal算法,然后每次建边可以只建立两条边(与左边的田建边、与上边的田建边)。
AC代码【kruskal】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int MAXN = 1000005;
const int INF = 0x3f3f3f3f;
int field[MAXN], father[MAXN];
int n, m, N, ind = 1, node = 0;
struct Node
{
int a, b, c;
Node(){}
Node(int x, int y, int z)
{
a = x;
b = y;
c = z;
}
bool operator < (const Node& x) const
{
return c < x.c;
}
}edge[MAXN << 1];
void init(int maxn)
{
for (int i = 1; i <= maxn; i++)
{
father[i] = i;
}
ind = 1;
node = 0;
}
int find(int x)
{
if (father[x] == x)
{
return x;
}
return father[x] = find(father[x]);
}
bool merge(int x, int y)
{
x = find(x);
y = find(y);
if (x != y)
{
father[x] = y;
// 两个点未连接
return true;
}
// 两个点已连接
return false;
}
int kruskal()
{
// 对边排序,便于后续对边贪心
sort(edge,edge + node);
int ans = 0, cnt = 0;
for (int i = 0; i < node; i++)
{
// 每次取出一条边,判断两个点是否已经连接
if (merge(edge[i].a, edge[i].b))
{
ans += edge[i].c;
cnt++;
}
// 已经找到 N - 1 条边了
if (cnt == N - 1)
{
break;
}
}
return ans;
}
int main()
{
int T;
while(~scanf("%d",&T))
{
for (int t = 1; t <= T; t++)
{
scanf("%d%d",&n,&m);
// n * m 个田地
N = n * m;
init(N);
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
scanf("%d",&field[ind]);
// 和左边的田建边
if (j > 1)
{
edge[node++] = Node(ind, ind - 1,abs(field[ind] - field[ind - 1]));
}
// 和上面的田建边
if (i > 1)
{
edge[node++] = Node(ind, ind - m, abs(field[ind] - field[ind - m]));
}
ind++;
}
}
printf("Case #%d:\n%d\n",t,kruskal());
}
}
return 0;
}