带权值的并查集整理与练习题
博文:https://blog.csdn.net/yjr3426619/article/details/82315133
带全并查集
路径压缩,表达每个当前node与 当前node所在的并查集的root 之间的关系
并查集一定是单向连通的,所以一些node处理时 【node1,node2】 weight 可能需要把 一端的闭区间变为开区间
俩个相对信息求出绝对信息 --水题
例题 : HDU 3038
//带权值并查集 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 2e5 + 25; int dis[maxn]; int p[maxn]; int find(int u)//寻根 { if(u!=p[u]) { int tmp = p[u];//保存原来的根节点 p[u] = find(p[u]);//路径压缩 dis[u] += dis[tmp];//dis[tmp] 已经为tmp(即原来父节点)到根节点的距离所以加上本身自身到tmp的dis则为u到root的dis } return p[u]; } void merge(int u,int v,int value) { int f1 = find(u); int f2 = find(v); p[f1] = f2; dis[f1] = value + dis[v] - dis[u]; }//带权并查集合并 int main() { int n,m;//n节点,m条边 while(cin>>n>>m) { int ans = 0,u,v,value; for(int i=0;i<=n;++i) { dis[i] = 0; p[i] = i; } while(m--) {//图改成了从0开始(important) cin>>u>>v>>value; --u; if(find(u)==find(v)) { if((dis[u]-dis[v])!=value) ++ans; }else{ merge(u,v,value);//否则合并 } } cout<<ans<<endl; } }
hihoCoder 1515
//带权并查集
#include<iostream>
#include<cstdio>//不是同一集合,合并,否则不做操作
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e5 + 15;
int dis[maxn],p[maxn];//当前node到root的距离
int find(int u)
{
if(u!=p[u])
{
int tmp = p[u];
p[u] = find(p[u]);
dis[u] += dis[tmp];
}
return p[u];
}
int main()
{
int n,m,q;//n个节点,m条边,q个查询
while(cin>>n>>m>>q)
{
for(int v=1;v<=n;++v)
{
dis[v] = 0;
p[v] = v;
}
int u,v,value;
while(m--)
{
cin>>u>>v>>value;
int f1 = find(u);
int f2 = find(v);
if(f1!=f2)
{
p[f1] = f2;
dis[f1] = value + dis[v] - dis[u];
}
}
while(q--)
{
cin>>u>>v;
if(find(u)!=find(v))
cout<<-1<<endl;
else
cout<<dis[u] - dis[v]<<endl;
}
}
}
POJ 2492
//带权并查集(主要用于逻辑判断) //题量还是少了,思维不够...(真菜) #include<iostream>//边的权值代表 #include<cstdio> #include<cstring>//其实就是构建出了一符图,每一个集合内的node都与这个图的root有一个关系, //通过中间关系root判断条件是否矛盾 #include<algorithm>//POJ 2492 using namespace std; const int maxn = 2e3 + 20; int p[maxn],dis[maxn]; int find(int u) { if(p[u]!=u) { int tmp = p[u];//保存原来父节点 p[u] = find(p[u]); dis[u] = (dis[u] + dis[tmp]) % 2; } return p[u]; } int main() { int kase = 0,T,n,m,u,v; scanf("%d",&T); while(T--) { if(kase) printf("\n"); printf("Scenario #%d:\n",++kase); scanf("%d%d",&n,&m);//n个节点,m个异性 for(int v=1;v<=n;++v) { p[v] = v; dis[v] = 0; } bool flag = false; while(m--) { scanf("%d%d",&u,&v); if(flag) continue; int f1 = find(u); int f2 = find(v); if(f1==f2) { if(dis[u]==dis[v]) flag = true;//u ~ v 节点为相同性别 }else{ p[f1] = f2; dis[f1] = (1 + dis[v] - dis[u]) % 2; } } if(flag) printf("Suspicious bugs found!\n"); else printf("No suspicious bugs found!\n"); } }
POJ 1128
思路还是很简单的,权值为0为同类,1 为 A 吃 B,2 为 A B 被 B 吃
简单推下关系就好了
//#include<bits/stdc++.h>//带权并查集(处理相对问题) //#include<array> #include<iostream>//加了个关闭输入流,一直WA,感受到了测评姬深深恶意 #include<cstdio> #define inf (0x3f3f3f3f)//状态的选择一定是在merage时,so 不存在一条边既表示吃又表示被吃的关系,题量少了,入了坑... using namespace std;//POJ1128 食物链 const int maxn = 5e4 + 15; //array<int,maxn> p; //array<int,maxn> dis; int p[maxn],dis[maxn]; int find(int node) { if(node!=p[node]) { int tmp = p[node]; p[node] = find(p[node]); dis[node] = (dis[tmp] + dis[node]) % 3; } return p[node]; } int main() { int n,k; int cmd,u,v; cin>>n>>k; int ans = 0; for(int i=1;i<=n;++i) { p[i] = i; dis[i] = 0; } while(k--) { scanf("%d%d%d",&cmd,&u,&v); if(u>n||v>n||(cmd==2&&u==v)) { ++ans; continue; } int f1 = find(u); int f2 = find(v); if(f1!=f2) { p[f1] = f2; dis[f1] = (cmd - 1 + dis[v] - dis[u]) % 3; }else{ if(cmd-1!=(dis[u]-dis[v]+3)%3) ++ans; } } cout<<ans<<endl; return 0; }
POJ 2912 (逆向思维真重要)
#include<iostream>//带权并查集
#include<cstdio>
#include<algorithm>
using namespace std;//(important) 如何找到最先能确定裁判的位置(想了很久,真菜)
// 因为是每次枚举去尝试n个人,哪个人为裁判,如果选中了一个裁判编号,则证明其他人都不是裁判,所以其余n-1个人的枚举出现矛盾
// so 剩余的n - 1个出现的矛盾行数最晚的则为最早能确定选定的是裁判的行数
int n,m;
const int maxn = 512;
const int maxx = 2e3 + 48;
int p[maxn],dis[maxn],node1[maxx],node2[maxx];
char Cmp[maxx];
int find(int u)
{
if(p[u]!=u)
{
int parent = p[u];
p[u] = find(p[u]);
dis[u] = (dis[u] + dis[parent]) % 3;
}
return p[u];
}
int main()
{
int u,v;
char cmp;
while(scanf("%d%d",&n,&m)==2)
{// 0 表示等于 1 表示大于 2 表示小于
for(int i=0;i!=m;++i)
scanf("%d%c%d",&node1[i],&Cmp[i],&node2[i]);
int cnt = 0,pos = 0,person = -1;//可能为裁判的人 最近能判断
int i,j;
for(i = 0;i!=n;++i)//枚举每一个人
{
for(j=0;j!=n;++j)
{
p[j] = j;//森林
dis[j] = 0;
}//重置
for(j = 0;j!=m;++j)
{
if(node1[j]==i||node2[j]==i)
continue;
int value;
if(Cmp[j]=='=')
value = 0;
else if(Cmp[j]=='>')
value = 1;
else
value = 2;
//cout<<node1[j]<<" "<<node2[j]<<endl;
int f1 = find(node1[j]);
int f2 = find(node2[j]);
//cout<<f1<<" "<<f2<<endl;
if(f1 != f2)//如果不存在关系
{
p[f1] = f2;
dis[f1] = ( dis[node2[j]] + value - dis[node1[j]] + 3) % 3;
}else{
if(value != ( dis[node1[j]] - dis[node2[j]] + 3) % 3)
{//起冲突
pos = max(pos,j+1);
break;
}
}
}
if(j==m)
{
++cnt;
person = i;
}
}
if(!cnt)
printf("Impossible\n");
else if(cnt>1)
printf("Can not determine\n");
else{
printf("Player %d can be determined to be the judge after %d lines\n",person,pos);
}
}
}
POJ1456 水题,不知道为什么归为并查集....
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e4 + 32;
typedef long long i64;
int n;
typedef struct{
int value,weight;
}node;
bool cmp(const node& n1,const node& n2)
{
if(n1.value!=n2.value)
return n1.value > n2.value;
return n1.weight > n2.weight;
}
bool vis[maxn];
vector<node> v;
int main()
{
ios::sync_with_stdio(false); cin.tie(0),cout.tie(0);
while(cin>>n)
{
v.clear();
memset(vis,false,sizeof(vis));
node tmp;
for(int i=0;i!=n;++i)
{
cin>>tmp.value>>tmp.weight;
v.push_back(tmp);
}
sort(v.begin(),v.end(),cmp);
i64 sum = 0;
for(int i=0;i!=n;++i)
{
int pos = 0;
for(int j=v[i].weight;j>=1;--j)
{
if(!vis[j])
{
vis[j] = true;
pos = j;
break;
}
}
if(pos!=0)
sum += v[i].value;
}
cout<<sum<<'\n';
}
}
POJ 1984 https://vjudge.net/problem/POJ-1984
//一道比较有意思的题目,刚开始没管方向,带权并查集胡乱一搞
思路:因为求的是 节点 x 和 y 的 曼哈顿距离,所以单纯只计算节点之间距离会有问题,会出现样例中的情况,即可能存在更小的距离
所以要分为 disx 和 disy 俩个方向,它们表示的是与并查集中的根的相对关系(即可能为正也可能为负,正负表示方向,分别带权并查集计算与跟节点的距离就好了)
//#include<bits/stdc++.h>//带权并查集
#include<vector>
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 4e4 + 32;
int p[maxn],disx[maxn],disy[maxn];//分别计算根节点
typedef long long i64;
int n,m,q;
typedef struct{
int u,v,value;
char pos;
}node;
typedef struct{
int u,v,time,index;//index 查询的位值
}answer;
typedef struct{
int len,index;
}nodeEnd;
bool cmp(const answer& a1,const answer& a2)
{
return a1.time < a2.time;
}
bool cmp2(const nodeEnd& e1,const nodeEnd& e2)
{
return e1.index < e2.index;
}
vector<node> v;
vector<answer> an;
vector<nodeEnd> ne;
int find(int u)
{
if(p[u]!=u)
{
int parent = p[u];
p[u] = find(p[u]);
disx[u] += disx[parent];
disy[u] += disy[parent];
}
return p[u];
}
int main()
{
ios::sync_with_stdio(false); cin.tie(0),cout.tie(0);
while(cin>>n>>m)
{
v.clear();
an.clear();
ne.clear();
for(int i=1;i<=n;++i)
{
p[i] = i;
disx[i] = disy[i] = 0;
}//init()
node tmp;
for(int i=0;i!=m;++i)
{
cin>>tmp.u>>tmp.v>>tmp.value>>tmp.pos;
v.push_back(tmp);
}
answer tmpa;
cin>>q;
for(int i=0;i!=q;++i)
{
cin>>tmpa.u>>tmpa.v>>tmpa.time;
tmpa.index = i;
an.push_back(tmpa);
}
sort(an.begin(),an.end(),cmp);
int cnt = 0;//标签
for(int i=0;i!=m;++i)
{
int f1 = find(v[i].u);
int f2 = find(v[i].v);
if(f1 != f2)
{
p[f1] = f2;
if(v[i].pos=='N')
{
disy[f1] = v[i].value + disy[v[i].v] - disy[v[i].u];
disx[f1] = disx[v[i].v] - disx[v[i].u];//表达的是相对位置
}else
if(v[i].pos=='S')
{
disy[f1] = -v[i].value + disy[v[i].v] - disy[v[i].u];
disx[f1] = disx[v[i].v] - disx[v[i].u];
}
if(v[i].pos=='E')
{
disx[f1] = v[i].value + disx[v[i].v] - disx[v[i].u];
disy[f1] = disy[v[i].v] - disy[v[i].u];
}else
if(v[i].pos=='W')
{
disx[f1] = -v[i].value + disx[v[i].v] - disx[v[i].u];
disy[f1] = disy[v[i].v] - disy[v[i].u];
}
}//匹配
while(i+1==an[cnt].time)//当时间正好匹配
{
nodeEnd tmp;
tmp.index = an[cnt].index;//下标
f1 = find(an[cnt].u);
f2 = find(an[cnt].v);
if(f1 != f2)
{
tmp.len = -1;
}else{
tmp.len = abs(disx[an[cnt].u] - disx[an[cnt].v]) +
abs(disy[an[cnt].u] - disy[an[cnt].v]);
}
ne.push_back(tmp);
++cnt;
}
}
sort(ne.begin(),ne.end(),cmp2);
for(int i=0;i!=ne.size();++i)
cout<<ne[i].len<<'\n';
}
}
不怕万人阻挡,只怕自己投降。
posted on 2019-09-28 13:14 chengyulala 阅读(463) 评论(0) 收藏 举报
浙公网安备 33010602011771号