老师上课讲了并查集,作业是用课上讲的并查集来实现渗透模型。
代码模拟 50*50网格
试验次数10000次
并查集
在下面的代码中,主要涉及到并查集的应用
我写的代码中并查集的合并(uni())的策略是如果相邻的两个网格都是打开的,则使用合并这两个相邻的网格,是坐标大的网格等于坐标小的网格,即
if(proot<qroot) Tree[qroot]=proot;
else Tree[proot]=qroot;
经过一系列“随机打开一个点,然后进行联通(如果可以联通的话)”的操作,Tree并查集中可能出现将最上层和最下层联通的点,此时渗透成功。值相同的点代表联通,当然联通的点的在Tree中的值不一定是相同的,但是联通的点的根节点肯定都是在Tree[0-50]间(因为模拟的渗透模型是50*50网格),所以代码中判断是否渗透成功的judge()函数就是根据在最底层网格中,是否存在一个网格的跟节点是0-50之间的网格,如果存在,则最上层与最下层联通,表示渗透成功。
什么是渗透模型
模拟随机打开点,直到渗透成功
模拟打开点,在下面的代码中写的函数produce()即模拟打开网格,
rand()随机产生一个数,处理之后表示随你打开的网格
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
#define N 50 //网格边数
#define SUM 2500 //网格总数
#define NUM 10000 //试验次数
int S[N][N]; //定义网格
int Tree[SUM]; //定义并查集树
void init(){ //表格和并查集初始化
for(int i=0;i<SUM;i++) Tree[i]=i;
for(int i=0;i<N;i++){
for(int j=0;j<N;j++) S[i][j]=0;
}
}
int fd(int p){ //find //查找根节点(根网格)
while(p!=Tree[p]){
p=Tree[p];
}
return p;
}
void uni(int p, int q){ //union 合并相邻的网格
//cout <<5 << endl;
int proot=fd(p);
int qroot=fd(q);
if(proot<qroot) Tree[qroot]=proot;
else Tree[proot]=qroot;
}
void produce(){ //执行此函数,随机打开一个网格
int x,y,index;
while(true){ //保证打开的是关闭着的网格
index=rand()%SUM;
x=index/N;
y=index%N;
//cout << index << "(" << x <<"," << y << ")" << S[x][y] << endl;
if(S[x][y]==0) break;
}
//cout << "(" << x <<"," << y << ")" << index << endl;
S[x][y]=1; //打开一个点
//与上下左右联通
if(index<SUM-N && S[x+1][y]==1) uni(index,index+N);//与下连通
//cout << 1 << endl;
if(index%N!=0 && S[x][y-1]==1) uni(index,index-1); //与左连通
//cout << 2 << endl;
if(index%N!=N-1 && S[x][y+1]==1) uni(index,index+1); //与右连通
//cout << 3 << endl;
if(index>=N && S[x-1][y]==1) uni(index,index-N); //与上连通
//cout << 4 << endl;
}
bool judge(){ //判断网格是否渗透
for(int i=SUM-N;i<SUM;i++)
if(fd(i)<N){return true;}
return false;
}
void p(){ //输出可以渗透的网格,测试用
for(int i=0;i<N;i++){
for(int j=0;j<N;j++) cout << S[i][j] << " ";
cout << endl;
}
}
int main()
{
double n=0; //用于接收总共打开的网格数
srand((unsigned)time(0));
for(int i=0;i<NUM;i++){ //模拟NUM次试验
init();
int cnt=0;
while(!judge()){
produce();
cnt++;
}
//p();
cout << "渗透成功。 总网格数:" << SUM << " " << "打开网格数:" << cnt << endl;
n+=(double)cnt;
}
cout << "最终模拟阈值:" <<n/(double)(SUM*NUM) << endl; // 打开网格数/总网格数=概率
return 0;
}
代码运行结果
原创博客,侵权必究