带权值的并查集整理与练习题
博文: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 阅读(434) 评论(0) 编辑 收藏 举报