带权并查集入门
2015-10-13 18:45
232 查看
并查集的升级版———用于记录节点之间的关系,可用于判断从属,敌对或者。。。
先看三个神似的题:
codevs 4246【noip模拟赛2015,9,day2】 奶牛的身高
http://codevs.cn/problem/4246/
给定n头牛之间的身高差(共m行),判断是否矛盾
只需要在unite时维护一个差值(祖先和当前节点的差值)
一开始敲对了,结果看到了负数。。。
于是深思熟虑地改了一改,然后悲伤的爆0!!!。。。
第二个:HDU 3038 How many answers are wrong
http://acm.hdu.edu.cn/showproblem.php?pid=3038
乱入的插图是神马???
题意:已知一串数字,下面是m句话:
表示A~B之间的和为S
然后看神似的代码:
当天下午昨晚的这个题晚上就考还爆零,我也是无语了。。。
然后是本校的X大神和我说的另一个神似。。。
BZOJ1202 狡猾的商人
http://www.lydsy.com/JudgeOnline/problem.php?id=1202
神似代码如下:
再来看一个与生物有关的题:
POJ 1182 食物链
http://poj.org/problem?id=1182
POJ少有的中文题,题意就不说了。。。
然而本人太弱,看完网上大神的题解差点跪了。。。
我也是佩服能把题解写的这么详细。。。OTZ
直接附代码吧。。。
然而在《挑战程序设计竞赛》中的做法相当简单
(但内存。。。):
创建三个数组,T[MAX_K],X[MAX_k],Y[MAX_k]
对于每种动物有i-A,i-B,i-C三个集合,分别代表A吃B,B吃C,C吃A
在处理时先判断合并是否会出现矛盾
若X,Y同类,合并X-A和Y-A,X-B和Y-B,X-C和Y-C
若X吃Y,合并X-A和Y-B,X-B和Y-C,X-C和Y-A
代码如下:
其实除了内存差别其他的还可以
POJ 1417 True Liars
http://poj.org/problem?id=1417
一个很神的题。。。正如某大神说的,并查集怎么还扯上DP了
(+﹏+)~。。。
然后看了网上众大神对于同一算法的不同解释,
终于,终于恍然大悟!!!!
是本人太弱,不得不服
直接贴代码吧。。。
不忘初心,才能始终
先看三个神似的题:
codevs 4246【noip模拟赛2015,9,day2】 奶牛的身高
http://codevs.cn/problem/4246/
给定n头牛之间的身高差(共m行),判断是否矛盾
只需要在unite时维护一个差值(祖先和当前节点的差值)
sum[a]=wi+sum[B]-sum[A]; //画棵树应该好想一些 sum[a]+=sum[fa[a]];
一开始敲对了,结果看到了负数。。。
于是深思熟虑地改了一改,然后悲伤的爆0!!!。。。
#include<cstdio> #define MAXN 30000+5 int fa[MAXN],sum[MAXN],n; bool flag; int find(int a) { if(fa[a]==a) return a; int t=find(fa[a]); sum[a]=sum[a]+sum[fa[a]]; return fa[a]=t; } void unite(int A,int B,int wi) { int a=find(A); int b=find(B); if(a==b) { if(sum[A]-sum[B]!=wi) flag=true; return; } fa[a]=b; sum[a]=wi+sum[B]-sum[A]; return ; } void init() { flag=false; for(int i=1;i<=n;i++) { fa[i]=i; sum[i]=0; } } int main() { int T,m,a,b,wi; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); init(); for(int i=1;i<=m;i++) { scanf("%d%d%d",&a,&b,&wi); if(!flag) unite(a,b,wi); } if(flag) printf("Bessie is blind.\n"); else printf("Bessie's eyes are good\n"); } }
第二个:HDU 3038 How many answers are wrong
http://acm.hdu.edu.cn/showproblem.php?pid=3038
乱入的插图是神马???
题意:已知一串数字,下面是m句话:
表示A~B之间的和为S
然后看神似的代码:
#include<cstdio> #define MAXN 200000+5 int fa[MAXN],rank[MAXN],sum[MAXN],n; int find(int a) { if(a==fa[a]) return a; int t=find(fa[a]); sum[a]=sum[a]+sum[fa[a]]; return fa[a]=t; } int _abs(int a,int b) { if(a<b) return b-a; else return a-b; } bool unite(int A,int B,int wi) { int a=find(A); int b=find(B); if(a==b) { if(_abs(sum[A],sum[B])==wi) return false; else return true; } fa[a]=b; sum[a]=sum[B]+wi-sum[A]; return false; } void init() { for(int i=0;i<=n;i++) { rank[i]=0; fa[i]=i; sum[i]=0; } } int main() { int m,l,r,wi; while(scanf("%d%d",&n,&m)!=EOF) { init(); int ans=0; sum[0]=0; for(int i=1;i<=m;i++) { scanf("%d%d%d",&l,&r,&wi); l--; if(unite(l,r,wi)) ans++; } printf("%d\n",ans); } }
当天下午昨晚的这个题晚上就考还爆零,我也是无语了。。。
然后是本校的X大神和我说的另一个神似。。。
BZOJ1202 狡猾的商人
http://www.lydsy.com/JudgeOnline/problem.php?id=1202
神似代码如下:
#include <cstdio> #include <cstring> #define MAXN 110 int fa[MAXN],sum[MAXN],n,m; bool flag; int find(int a) { if(a==fa[a]) return a; else { int f=find(fa[a]); sum[a]+=sum[fa[a]]; return fa[a]=f; } } void unite(int a,int b,int wi) { int ra=find(a); int rb=find(b); if(ra==rb) { if(sum[b]-sum[a]!=wi) flag=false; return ; } else { fa[ra]=rb; sum[ra]=sum[b]-sum[a]-wi; } } void init() { for(int i=0;i<=n;i++) { fa[i]=i; sum[i]=0; } } int main() { int T; int a,b,wi; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); init(); flag=true; for(int i=1;i<=m;i++) { scanf("%d%d%d",&a,&b,&wi); if(flag) unite(a-1,b,wi); } if(!flag) printf("false\n"); else printf("true\n"); } return 0; }
再来看一个与生物有关的题:
POJ 1182 食物链
http://poj.org/problem?id=1182
POJ少有的中文题,题意就不说了。。。
然而本人太弱,看完网上大神的题解差点跪了。。。
我也是佩服能把题解写的这么详细。。。OTZ
直接附代码吧。。。
#include <cstdio> #include <cstring> #define MAXN 50000+5 int fa[MAXN],rank[MAXN],n,k; int find(int a) { if(a==fa[a]) return a; else { int f=find(fa[a]); rank[a]=(rank[a]+rank[fa[a]])%3; return fa[a]=f; } } bool unite(int a,int b,int d) { int ra=find(a); int rb=find(b); if(ra==rb) { if((rank[b]-rank[a]+3)%3!=d) return true; else return false; } fa[rb]=ra; rank[rb]=(rank[a]-rank[b]+d+3)%3; return false; } void init() { for(int i=1;i<=n;i++) { rank[i]=0; fa[i]=i; } } int main() { int sum=0; int d,x,y; scanf("%d%d",&n,&k); init(); for(int i=1;i<=k;i++) { scanf("%d%d%d",&d,&x,&y); if(x>n||y>n||(x==y&&d==2)) sum++; else if(unite(x,y,d-1)) sum++; } printf("%d\n",sum); return 0; }
然而在《挑战程序设计竞赛》中的做法相当简单
(但内存。。。):
创建三个数组,T[MAX_K],X[MAX_k],Y[MAX_k]
对于每种动物有i-A,i-B,i-C三个集合,分别代表A吃B,B吃C,C吃A
在处理时先判断合并是否会出现矛盾
若X,Y同类,合并X-A和Y-A,X-B和Y-B,X-C和Y-C
若X吃Y,合并X-A和Y-B,X-B和Y-C,X-C和Y-A
代码如下:
#include <cstdio> #include <cstring> #define MAXN 50000*3+5 #define MAX_K 100000 int T[MAX_K],X[MAX_K],Y[MAX_K]; int N,K,fa[MAXN]; int find(int a) { if(fa[a]==a) return a; else return fa[a]=find(fa[a]); } void unite(int a,int b) { a=find(a); b=find(b); if(a==b) return ; else { fa[a]=b; return ; } } bool same(int a,int b) { return find(a)==find(b); } void init(int n) { for(int i=1;i<=n;i++) fa[i]=i; } void solve() { init(3*N); int ans=0; for(int i=0;i<K;i++) { int t=T[i]; int x=X[i]-1,y=Y[i]-1; if(x<0||N<=x||y<0||N<=y) { ans++; continue; } if(t==1) { if(same(x,y+N)||same(x,y+2*N)) ans++; else { unite(x,y); unite(x+N,y+N); unite(x+N*2,y+N*2); } } else { if(same(x,y)||same(x,y+2*N)) ans++; else { unite(x,y+N); unite(x+N,y+2*N); unite(x+2*N,y); } } } printf("%d\n",ans); } int main() { scanf("%d%d",&N,&K); for(int i=0;i<K;i++) scanf("%d%d%d",&T[i],&X[i],&Y[i]); solve(); return 0; }
其实除了内存差别其他的还可以
POJ 1417 True Liars
http://poj.org/problem?id=1417
一个很神的题。。。正如某大神说的,并查集怎么还扯上DP了
(+﹏+)~。。。
然后看了网上众大神对于同一算法的不同解释,
终于,终于恍然大悟!!!!
是本人太弱,不得不服
直接贴代码吧。。。
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; #define N 605 int p,p1,p2; int a [2],pre ,r ; vector<int > b [2]; int dp [N/2]; int cnt; bool vis ; int find(int a) { if(a!=pre[a]) { int f=pre[a]; pre[a]=find(pre[a]); r[a]=r[a]^r[f]; } return pre[a]; } void init() { for(int i=1;i<=p1+p2;i++) { pre[i]=i; r[i]=0; } memset(vis,false,sizeof(vis)); memset(a,0,sizeof(a)); cnt=1; for(int i=1;i<N;i++) { b[i][0].clear(); b[i][1].clear(); } } int main() { while(scanf("%d%d%d",&p,&p1,&p2)!=EOF&&p+p1+p2) { init(); while(p--) { int u,v; char s[10]; scanf("%d%d%s",&u,&v,s); int k=(s[0]=='n'); int ra=find(u),rb=find(v); if(ra!=rb) { pre[ra]=rb; r[ra]=r[u]^r[v]^k; } } for(int i=1;i<=p1+p2;i++) { if(!vis[i]) { int f=find(i); for(int j=i;j<=p1+p2;j++) { if(find(j)==f) { vis[j]=true; b[cnt][r[j]].push_back(j); a[cnt][r[j]]++; } } cnt++; } } memset(dp,0,sizeof(dp)); dp[0][0]=1; for(int i=1;i<cnt;i++) { for(int j=p1;j>=0;j--) { if(j-a[i][0]>=0) dp[i][j]+=dp[i-1][j-a[i][0]]; if(j-a[i][1]>=0) dp[i][j]+=dp[i-1][j-a[i][1]]; } } if(dp[cnt-1][p1]!=1) { printf("no\n"); continue; } else { vector<int > ans; ans.clear(); for(int i=cnt-1;i>=1;i--) { if(p1-a[i][0]>=0&&p2-a[i][1]>=0&&dp[i-1][p1-a[i][0]]==1) { for(int j=0;j<b[i][0].size();j++) ans.push_back(b[i][0][j]); p1-=a[i][0]; p2-=a[i][1]; } else if(p1-a[i][1]>=0&&p2-a[i][0]>=0&&dp[i-1][p1-a[i][1]]==1) { for(int j=0;j<b[i][1].size();j++) ans.push_back(b[i][1][j]); p1-=a[i][1]; p2-=a[i][0]; } } sort(ans.begin(),ans.end()); for(int i=0;i<ans.size();i++) printf("%d\n",ans[i]); printf("end\n"); } } return 0; }
不忘初心,才能始终
相关文章推荐
- (算法)最长不重复子串
- python学习--文件I/O操作
- 杭电hdu2001 计算两点间的距离 每行4个数 C++
- lr数据库参数化取数:The query result is empty and same is the parameter file问题原因
- 干货 | Java 中的 String 为什么是不可变的?
- spark学习4-倾斜数据join
- Linux下静态库_库的基本概念;如何生成静态库动态库;nm查看库中包含那些函数、ar生成静态库,查看库中包含那些.o文件、ldd查看程序依赖的.so文件;gcc/g++与库相关的参数-L,-l,-f
- android使用timer检测网络请求时长
- Android中Application的onCreate调用多次的解决方案
- velocity 转json数组 相关字段使用i18n
- LeetCode 1:Two Sum
- 【Unity】【翻译搬运】使用WheelColliders做出稳定真实的车
- CentOS-6.3安装配置JDK-7
- ubuntu /tmp mounted overflow when there is free space
- 现状与未来中国光纤通信,如何发展产业?
- Pixi
- Android性能优化之电量篇
- LeetCode273:Integer to English Words
- ocp-390
- spring操作事务的两种基本方式