P3366 【模板】最小生成树
prim 算法邻接矩阵存图
//prim
#include <bits/stdc++.h>
using namespace std;
int n, m, g[5010][5010], minn[5010], asd, u, v, w;
bool vis[5010];
int main()
{
scanf("%d %d", &n, &m);
memset(g, 0x3f, sizeof(g));
memset(minn, 0x3f, sizeof(minn));
while(m--){
scanf("%d %d %d", &u, &v, &w);
g[u][v]=min(g[u][v], w);
g[v][u]=min(g[v][u], w);
}
minn[1]=0;
for(int i=1; i<=n; ++i){
int minid=0;
int minedge=0x3f3f3f3f;
for(int j=1; j<=n; ++j){
if(!vis[j] && minn[j]<minedge){
minid=j;
minedge=minn[j];
}
}
vis[minid]=true;
for(int j=1; j<=n; ++j){
//必须确保j是没加到生成树的点,才可更新 minn 值
if(!vis[j]){
minn[j]=min(minn[j], g[minid][j]);
}
}
}
for(int i=1; i<=n; ++i){
if(minn[i]==0x3f3f3f3f){
puts("orz");
return 0;
}
}
for(int i=1; i<=n; ++i){
asd+=minn[i];
}
printf("%d\n", asd);
return 0;
}
prim 算法链式前向星存图
//prim
#include <bits/stdc++.h>
using namespace std;
int n, m, minn[5010], asd, x, y, z, tot, head[5010];
bool vis[5010];
struct node
{
int to, dis, nex;
}e[400010];
void add(int u, int v, int w)
{
tot++;
e[tot].to=v;
e[tot].dis=w;
e[tot].nex=head[u];
head[u]=tot;
}
int main()
{
scanf("%d %d", &n, &m);
memset(minn, 0x3f, sizeof(minn));
while(m--){
scanf("%d %d %d", &x, &y, &z);
add(x, y, z);
add(y, x, z);
}
minn[1]=0;
for(int i=1; i<=n; ++i){
int minid=0;
int minedge=0x3f3f3f3f;
for(int j=1; j<=n; ++j){
if(!vis[j] && minn[j]<minedge){
minid=j;
minedge=minn[j];
}
}
vis[minid]=true;
for(int j=head[minid]; j; j=e[j].nex){
int v=e[j].to;
int w=e[j].dis;
//必须确保j是没加到生成树的点,才可更新 minn 值
if(!vis[v]){
minn[v]=min(minn[v], w);
}
}
}
for(int i=1; i<=n; ++i){
if(minn[i]==0x3f3f3f3f){
puts("orz");
return 0;
}
}
for(int i=1; i<=n; ++i){
asd+=minn[i];
}
printf("%d\n", asd);
return 0;
}
prim堆优化版本
//prim 堆优化版本
#include <bits/stdc++.h>
using namespace std;
int n, m, minn[5010], asd, x, y, z, tot, head[5010];
bool vis[5010];
//定义优先队列里的元素
struct Node
{
int id, dis;
}edge;
priority_queue<Node> q;
//运算符重载
bool operator <(Node a, Node b)
{
return a.dis>b.dis;
}
//链式前向星
struct node
{
int to, dis, nex;
}e[400010];
//链式前向星建边
void add(int u, int v, int w)
{
tot++;
e[tot].to=v;
e[tot].dis=w;
e[tot].nex=head[u];
head[u]=tot;
}
//prim堆优化版
void prim()
{
//默认1号点开始
edge.id=1;
edge.dis=0;
minn[1]=0;
q.push(edge);
while(!q.empty()){
edge=q.top();
q.pop(); //lg的复杂度
int u=edge.id;
if(vis[u]){ //优化复杂度
continue;
}
vis[u]=true; //点u加到生成树中
//一人得道,鸡犬升天
for(int i=head[u]; i; i=e[i].nex){
int v=e[i].to;
int w=e[i].dis;
//必须确保v是没加到生成树的点,才可能更新 minn 值
if(!vis[v] && w<minn[v]){
minn[v]=w;
edge.id=v;
edge.dis=w;
q.push(edge);
}
}
}
}
int main()
{
scanf("%d %d", &n, &m);
memset(minn, 0x3f, sizeof(minn));
while(m--){
scanf("%d %d %d", &x, &y, &z);
add(x, y, z);
add(y, x, z);
}
prim();
for(int i=1; i<=n; ++i){
asd+=minn[i];
if(minn[i]==0x3f3f3f3f){
puts("orz");
return 0;
}
}
printf("%d\n", asd);
return 0;
}
kruskal算法
//kruskal
#include <bits/stdc++.h>
using namespace std;
int n, m, x, y, fx, fy, cnt, ans;
int fa[5001];
struct edge
{
int u, v, w;
}e[200000];
bool cmp(edge x, edge y)
{
return x.w<y.w;
}
int find(int a)
{
if(fa[a]==a) return a;
else return fa[a]=find(fa[a]);
}
int main()
{
cin >> n >> m;
for(int i=1; i<=n; i++) fa[i]=i;
for(int i=0; i<m; i++){
cin >> e[i].u >> e[i].v >> e[i].w;
}
sort(e, e+m, cmp);
for(int i=0; i<m; i++){
fx=find(e[i].u);
fy=find(e[i].v);
if(fx!=fy){
cnt++;
ans+=e[i].w;
fa[fx]=fy;
if(cnt==n-1)
break;
}
}
if(cnt==n-1)
cout << ans;
else
cout << "orz";
return 0;
}
P2872 [USACO07DEC]Building Roads S
#include <bits/stdc++.h>
using namespace std;
int n, m, u, v, fa[1010], total;
double x[1010], y[1010], ans;
struct node
{
int from, to;
double dis;
}edge[1000010];
bool cmp(node a, node b)
{
return a.dis<b.dis;
}
int find(int a)
{
if(a!=fa[a]){
return fa[a]=find(fa[a]);
}
else{
return fa[a];
}
}
void merge(int a, int b)
{
int u=find(a);
int v=find(b);
if(u!=v){
fa[u]=v;
}
}
double cal(int i, int j)
{
return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
int main()
{
scanf("%d %d", &n, &m);
for(int i=1; i<=n; ++i){
fa[i]=i;
scanf("%lf %lf", &x[i], &y[i]);
}
for(int i=1; i<=m; ++i){
scanf("%d %d", &u, &v);
if(find(u)!=find(v)){
merge(u, v);
}
}
for(int i=1; i<n; ++i){
for(int j=i+1; j<=n; ++j){
total++;
edge[total].from=i;
edge[total].to=j;
edge[total].dis=cal(i, j);
}
}
sort(edge+1, edge+total+1, cmp);
for(int i=1; i<=total; ++i){
u=edge[i].from;
v=edge[i].to;
if(find(u)!=find(v)){
merge(u, v);
ans+=edge[i].dis;
}
}
printf("%.2lf", ans);
return 0;
}
P2121 拆地毯
#include <bits/stdc++.h>
using namespace std;
int n, m, k, u, v, fa[100010], cnt, ans;
struct node
{
int from, to;
int w;
}edge[100010];
bool cmp(node a, node b)
{
return a.w>b.w;
}
int find(int a)
{
if(a!=fa[a]){
return fa[a]=find(fa[a]);
}
else{
return fa[a];
}
}
void merge(int a, int b)
{
int u=find(a);
int v=find(b);
if(u!=v){
fa[u]=v;
}
}
int main()
{
scanf("%d %d %d", &n, &m, &k);
for(int i=1; i<=n; ++i){
fa[i]=i;
}
for(int i=1; i<=m; ++i){
scanf("%d %d %d", &edge[i].from, &edge[i].to, &edge[i].w);
}
sort(edge+1, edge+m+1, cmp);
for(int i=1; i<=m; ++i){
u=edge[i].from;
v=edge[i].to;
if(find(u)!=find(v)){
merge(u, v);
ans+=edge[i].w;
cnt++;
if(cnt==k){
printf("%d", ans);
return 0;
}
}
}
return 0;
}
P1195 口袋的天空
#include <bits/stdc++.h>
using namespace std;
int n, m, k, u, v, fa[1010], cnt, ans;
struct node
{
int from, to;
int dis;
}edge[10010];
bool cmp(node a, node b)
{
return a.dis<b.dis;
}
int find(int a)
{
if(a!=fa[a]){
return fa[a]=find(fa[a]);
}
else{
return fa[a];
}
}
void merge(int a, int b)
{
int u=find(a);
int v=find(b);
if(u!=v){
fa[u]=v;
}
}
int main()
{
scanf("%d %d %d", &n, &m, &k);
for(int i=1; i<=n; ++i){
fa[i]=i;
}
for(int i=1; i<=m; ++i){
scanf("%d %d %d", &edge[i].from, &edge[i].to, &edge[i].dis);
}
sort(edge+1, edge+m+1, cmp);
for(int i=1; i<=m; ++i){
u=edge[i].from;
v=edge[i].to;
if(find(u)!=find(v)){
merge(u, v);
ans+=edge[i].dis;
cnt++;
if(cnt+k==n){
printf("%d", ans);
return 0;
}
}
}
printf("No Answer");
return 0;
}
P1536 村村通
#include <bits/stdc++.h>
using namespace std;
int n, m, ans, tot, fa[1010], cnt;
bool connect[1010][1010];
struct node
{
int from, to;
}edge[500010];
void init()
{
memset(connect, 0, sizeof(connect));
tot=0;
cnt=0;
for(int i=1; i<=n; ++i){
fa[i]=i;
}
}
int find(int x)
{
if(fa[x]!=x){
return fa[x]=find(fa[x]);
}
else return x;
}
void merge(int u, int v)
{
int fu=find(u);
int fv=find(v);
if(fu!=fv){
fa[fu]=fv;
}
}
int main()
{
while(scanf("%d", &n) && n!=0){
int u, v;
scanf("%d", &m);
init();
while(m--){
scanf("%d %d", &u, &v);
if(u!=v && !connect[u][v]){
connect[u][v]=connect[v][u]=true;
tot++;
edge[tot].from=u;
edge[tot].to=v;
}
}
for(int i=1; i<=tot; ++i){
u=edge[i].from;
v=edge[i].to;
int fu=find(u);
int fv=find(v);
if(fu!=fv){
cnt++;
merge(fu, fv);
}
}
if(cnt>=n-1){
printf("0\n");
}
else{
printf("%d\n", n-1-cnt);
}
}
return 0;
}
P4047 [JSOI2010]部落划分
#include <bits/stdc++.h>
using namespace std;
int n, k, cnt, fa[1010], numk;
struct edge{
int u, v;
double dis;
}e[1000000];
struct node
{
double x, y;
}a[1010];
double cal(int u, int v)
{
return sqrt((a[u].x-a[v].x)*(a[u].x-a[v].x)+(a[u].y-a[v].y)*(a[u].y-a[v].y));
}
//从小到大排序
bool cmp(edge x, edge y)
{
return x.dis<y.dis;
}
int find(int x)
{
if(x==fa[x]){
return x;
}
return fa[x]=find(fa[x]);
}
void merge(int u, int v)
{
int fu=find(u);
int fv=find(v);
if(fu==fv){
return;
}
fa[fu]=fv;
}
int main()
{
scanf("%d %d", &n, &k);
for(int i=1; i<=n; ++i){
scanf("%lf %lf", &a[i].x, &a[i].y);
fa[i]=i;
}
for(int i=1; i<=n; ++i){ //将每条边都存起来
for(int j=i+1; j<=n; ++j){
cnt++;
e[cnt].u=i;
e[cnt].v=j;
e[cnt].dis=cal(i, j);
}
}
sort(e+1, e+cnt+1, cmp); //根据边权从小到大排序
numk=n;
for(int i=1; i<=cnt; ++i){ //从最近的野人开始依次枚举
int fu=find(e[i].u);
int fv=find(e[i].v);
if(fu!=fv){ //如果这两个野人没在同一个部落, 则需要合并
merge(fu, fv);
numk--; //没连一条边, 则部落数减1
if(numk==k){ //当部落数恰好为k时, 则不能再连边了
for(int j=i+1; j<=cnt; ++j){ //找距离最近的部落
fu=find(e[j].u);
fv=find(e[j].v);
if(fu!=fv){ //如果不在同一部落, 则找到答案
printf("%.2lf\n", e[j].dis);
return 0;
}
}
}
}
}
return 0;
}
P2504 [HAOI2006]聪明的猴子
#include <bits/stdc++.h>
using namespace std;
int n, m, a[505], ans;
double g[1005][1005], flag;
bool vis[1005];
double minn[1005];
struct node{
double x,y;
}site[1005];
double cal(int u, int v)
{
return sqrt((site[u].x-site[v].x)*(site[u].x-site[v].x) + (site[u].y-site[v].y)*(site[u].y-site[v].y));
}
int main()
{
cin >> m; //表示猴子的个数
for(int i=1; i<=m; ++i){
cin >> a[i]; //依次表示猴子的最大跳跃距离
}
cin >> n; //表示树的总棵数
for(int i=1; i<=n; ++i){
cin >> site[i].x >> site[i].y;
minn[i]=1000000000;
}
for(int i=1; i<=n; ++i){
for(int j=i+1; j<=n; ++j){
g[i][j]=g[j][i]=cal(i, j);
}
minn[i]=min(minn[i], g[i][1]);
}
vis[1]=true;
minn[1]=0;
for(int i=1; i<=n; ++i){
flag=1000000000;
int u;
for(int j=1; j<=n; ++j){
if(minn[j]<flag && vis[j]==false){
u=j;
flag=minn[j];
}
}
vis[u]=true;
for(int j=1; j<=n; ++j){
if(g[u][j]<minn[j] && vis[j]==false){
minn[j]=g[u][j];
}
}
}
flag=0;
for(int i=2; i<=n; ++i){
flag=max(flag, minn[i]);
}
for(int i=1; i<=m; ++i){
if(a[i]>=flag) ans++;
}
cout << ans;
return 0;
}
T112649 走廊泼水节
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int fat[6001], n, T, ans, i;
int num[6001];//记录并查集 集合里点的数量
void init(){
for(int i=0;i<6001;i++){
fat[i]=i;
num[i]=1;
}
ans=0;
}
int father(int x)
{
if(fat[x]!=x)
fat[x]=father(fat[x]);
return fat[x];
}
struct Node{
int a,b,c;
}a[6001];
int cmp(Node a,Node b){
return a.c<b.c;
}
int main(){
cin >> T;
while(T--){
init();
cin >> n;
for(i=0; i<n-1; i++){
cin >> a[i].a >> a[i].b >> a[i].c;
}
sort(a, a+n-1, cmp);
for(i=0; i<n-1; i++){
int x=father(a[i].a);
int y=father(a[i].b);
if(x==y)continue;
ans+=(num[x]*num[y]-1)*(a[i].c+1);
fat[x]=y;
num[y]+=num[x];
}
cout << ans << endl;
}
return 0;
}
P1991 无线通讯网(好题)
#include <bits/stdc++.h>
using namespace std;
//s可安装的卫星电话的哨所数, p边防哨所的总数
int s, p, x[510], y[510], total, fa[510], cnt;
struct node
{
int u, v;
double dis;
}edge[250010];
double cal(int i, int j)
{
return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
bool cmp(node x, node y) //根据距离从小到大排序
{
return x.dis<y.dis;
}
int find(int x)
{
if(fa[x]==x){
return x;
}
else{
return fa[x]=find(fa[x]);
}
}
void merge(int a, int b)
{
int u=find(a);
int v=find(b);
if(u==v) return;
fa[u]=v;
}
int main()
{
scanf("%d %d", &s, &p);
for(int i=1; i<=p; ++i){
fa[i]=i;
scanf("%d %d", &x[i], &y[i]);
}
if(s>=p){
printf("0.00");
return 0;
}
for(int i=1; i<p; ++i){
for(int j=i+1; j<=p; ++j){
total++;
edge[total].u=i;
edge[total].v=j;
edge[total].dis=cal(i, j);
}
}
sort(edge+1, edge+total+1, cmp);
//现在总共有s个点安装了卫星电话, 还有p-s个点没有加入网络中,
//把装了卫星电话的哨所看出一个点, 相当于p-s+1个点求最小生成树, 需要连p-s条边,
//要想求最小的收发器通话距离 D, 那么就必须先满足最小生成树,
//满足了最小生成树后, 不用管卫星电话怎么设置, 生成树中第p-s条边的长度就是答案。
//而不是先按照最大边先设卫星电话, 再求最小生成树, 这样可能会影响答案
for(int i=1; i<=total; ++i){
int asd1, asd2;
asd1=edge[i].u;
asd2=edge[i].v;
int fa1=find(asd1);
int fa2=find(asd2);
if(fa1!=fa2){
merge(fa1, fa2);
cnt++;
}
if(cnt==p-s){
printf("%.2lf", edge[i].dis);
return 0;
}
}
return 0;
}
P1194 买礼物
#include <bits/stdc++.h>
using namespace std;
int n, m, x, price, total, fa[510], asd1, asd2, cnt, ans;
//边
struct node
{
int u, v, w;
}e[130000];
//根据价格从小到大排序
bool cmp(node x, node y)
{
return x.w<y.w;
}
//并查集查找祖先
int find(int x)
{
if(fa[x]==x){
return x;
}
else{
return fa[x]=find(fa[x]);
}
}
//并查集合并
void merge(int u, int v)
{
int fu=find(u);
int fv=find(v);
if(fu!=fv){
fa[fu]=fv;
}
}
int main()
{
scanf("%d %d", &price, &n);
for(int i=1; i<=n; ++i){
fa[i]=i; //并查集初始化
for(int j=1; j<=n; ++j){
scanf("%d", &x);
if(j>i){ //只存一条边就行了
if(x>price || x==0){ //太贵或者没有优惠, 按原价来
x=price;
}
total++;
e[total].u=i;
e[total].v=j;
e[total].w=x;
}
}
}
sort(e+1, e+total+1, cmp);
//Kruskal
for(int i=1; i<=total; ++i){ //枚举边
asd1=e[i].u; //边的两端点
asd2=e[i].v; //边的两端点
//如果这两点没有连通起来
if(find(asd1)!=find(asd2)){
cnt++;
ans+=e[i].w;
merge(asd1, asd2); //连
if(cnt==n-1){ //全部连起来后
break;
}
}
}
ans+=price;
printf("%d", ans);
return 0;
}
贡献两组数据
P1194_1.in
3 4
0 1 0 0
1 0 0 0
0 0 0 0
0 0 0 0
P1194_1.out
10
P1194_4.in
800 7
0 104 999 196 952 882 800
104 0 262 0 440 475 261
999 262 0 143 613 789 456
196 0 143 0 392 316 458
952 440 613 392 0 966 130
882 475 789 316 966 0 882
800 261 456 458 130 882 0
P1194_4.out
1950
P2212 [USACO14MAR]Watering the Fields S
#include <bits/stdc++.h>
using namespace std;
const int N=2010, M=2000010;
int n, c, fa[N], tot, cnt, ans;
struct node1
{
int x, y;
}point[N];
struct node
{
int u, v;
int dis;
}e[M];
double cal(int u, int v)
{
return (point[u].x-point[v].x)*(point[u].x-point[v].x)+(point[u].y-point[v].y)*(point[u].y-point[v].y);
}
bool cmp(node x, node y)
{
return x.dis<y.dis;
}
int find(int x)
{
if(fa[x]==x){
return x;
}
else{
return fa[x]=find(fa[x]);
}
}
void merge(int u, int v)
{
int fu=find(u);
int fv=find(v);
fa[fu]=fa[fv];
}
int main()
{
scanf("%d %d", &n, &c);
for(int i=1; i<=n; ++i){
scanf("%d %d", &point[i].x, &point[i].y);
fa[i]=i;
}
for(int i=1; i<n; ++i){
for(int j=i+1; j<=n; ++j){
tot++;
e[tot].u=i;
e[tot].v=j;
e[tot].dis=cal(i, j);
}
}
sort(e+1, e+tot+1, cmp);
for(int i=1; i<=tot; ++i){
if(e[i].dis>=c){
if(find(e[i].u)!=find(e[i].v)){
ans+=e[i].dis;
cnt++;
merge(e[i].u, e[i].v);
if(cnt==n-1){
break;
}
}
}
}
if(cnt==n-1){
printf("%d", ans);
}
else{
printf("-1");
}
return 0;
}
CF 1245D - Shichikuji and Power Grid
一道生成树的好题,用kruskall做的
#include <bits/stdc++.h>
using namespace std;
int n, cnt, fa[2010], ans[2010], cnt1, cnt2, ok, firs;
long long c[2010], k[2010];
long long total;
struct asd
{
int u, v;
}ans2[2010];
struct point
{
long long x, y;
}a[2010];
struct node
{
int u, v;
long long dis;
}e[2002010];
inline bool cmp(node p, node q)
{
return p.dis<q.dis;
}
inline long long cal(int i, int j)
{
return (k[i]+k[j])*(abs(a[i].x-a[j].x)+abs(a[i].y-a[j].y));
}
inline int find(int x)
{
if(fa[x]==x){
return fa[x];
}
else{
return fa[x]=find(fa[x]);
}
}
inline void merge(int u, int v)
{
int fu=find(u);
int fv=find(v);
fa[fu]=fv;
}
int main()
{
scanf("%d", &n);
for(int i=1; i<=n; ++i){
scanf("%lld %lld", &a[i].x, &a[i].y);
fa[i]=i;
}
for(int i=1; i<=n; ++i){
scanf("%lld", &c[i]);
cnt++;
e[cnt].u=i;
e[cnt].v=0;
e[cnt].dis=c[i];
}
for(int i=1; i<=n; ++i){
scanf("%lld", &k[i]);
}
for(int i=1; i<n; ++i){
for(int j=i+1; j<=n; ++j){
cnt++;
e[cnt].u=i;
e[cnt].v=j;
e[cnt].dis=cal(i, j);
}
}
sort(e+1, e+cnt+1, cmp);
for(int i=1; i<=cnt; ++i){
//如果不是连边, 并且该点没通电
if(e[i].v==0 && find(e[i].u)!=find(0)){
cnt1++;
ans[cnt1]=e[i].u;
ok++;
total+=e[i].dis;
merge(e[i].u, 0); //让该点通电, 0号表示超级电源
}
else if(e[i].v!=0 && (find(e[i].u)!=find(e[i].v))){
//如果是连边, 并且该边的两端点没在一个联通块
cnt2++;
ans2[cnt2].u=e[i].u;
ans2[cnt2].v=e[i].v;
ok++;
merge(e[i].u, e[i].v);
total+=e[i].dis;
}
if(ok==n){ //因为至少需要一个点连接超级电源, 所以生成树的merge需要1+n-1=n
break;
}
}
printf("%lld\n", total);
printf("%d\n", cnt1);
for(int i=1; i<=cnt1; ++i){
printf("%d ", ans[i]);
}
printf("\n");
printf("%d\n", cnt2);
for(int i=1; i<=cnt2; ++i){
printf("%d %d\n", ans2[i].u, ans2[i].v);
}
return 0;
}