bfs是浪费空间节省时间,dfs是浪费时间节省空间。
dfs模板
// 数独 sudoku
#include
using namespace std;
int P[9][9];
bool flag = false;
// 处理输入
void input(){
char t;
cout << "输入棋盘,空用 0 表示\n";
for(int i = 0; i < 9; i ++){
for(int j = 0; j < 9; j ++){
cin >> t;
P[i][j] = t - '0';
}
}
}
// 处理输出
void output(){
for(int i = 0; i < 9; i ++){
for(int j = 0; j < 9; j ++){
if(P[i][j]){
cout << P[i][j];
}else{
cout << " ";
}
if(j%3 == 2) cout << " ";
}
if(i%3 == 2) cout << endl;
cout << endl;
}
}
// 判断即将放入的 是否符合条件
bool check(int n, int num){
// 行
for(int i = 0; i < 9; i ++){
if(P[n/9][i] == num){
return false;
}
}
// 列
for(int i = 0; i < 9; i ++){
if(P[i][n%9] == num){
return false;
}
}
// 宫
int x = n / 9 / 3 * 3;
int y = n % 9 / 3 * 3;
for(int i = 0; i < 3; i ++){
for(int j = 0; j < 3; j ++){
if(P[x+i][y+j] == num){
return false;
}
}
}
return true;
}
// [n/9][n%9]
void dfs(int n){
// 成功
if(n > 80){
flag = true;
return;
}
int x = n/9, y = n%9;
// 有值 跳过
if(P[x][y]){
dfs(n+1);
return;
}
// 遍历
for(int i = 1; i <= 9; i ++){
// 判断
if(check(n, i)){
// 判断成功 赋值
P[x][y] = i;
dfs(n+1);
// 退出时判断是否完成 完成时退出
if(flag){
return;
}
// 未完成 重置棋盘
P[x][y] = 0;
}
}
}
int main(){
input();
cout << endl << endl;
dfs(0);
output();
return 0;
}
/*
测试用例
170004000
360810054
008050109
007035048
000000270
000008601
000060080
700000000
000403016
*/
bfs模板
P1331 海战
#include
#define intn long long
#define _0for(i, a) for(int i = 0; i < (a); ++i)
#define _1for(i, a) for(int i = 1; i <=(a); ++i)
using namespace std;
int a1x,a1y;
int a2x,a2y;
int ans=0;
int bx[5]={0,0,0,1,-1};
int by[5]={0,1,-1,0,0};
char a[1005][1005];
int n,m;
struct node
{
int x,y;
};
int bfs(int x,int y)
{
int ans=1;
queue<node> q;
node p;
p.x=x,p.y=y;
q.push(p);
while(!q.empty())
{
int nowx=q.front().x,nowy=q.front().y;
a[nowx][nowy]='.';
for(int i=1;i<=4;i++)
{
int newx=bx[i]+nowx,newy=by[i]+nowy;
if(newx>=1&&newy>=1&&newx<=n&&newy<=m&&a[newx][newy]=='#')
{
q.push(node{newx,newy});
a[newx][newy]='.';
a1x=min(newx,a1x);
a1y=min(newy,a1y);
a2x=max(newx,a2x);
a2y=max(newy,a2y);
ans++;
}
}
q.pop();
}
return ans;
}
main(void)
{
while(cin>>n>>m)
{
int ans=0,flag=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(a[i][j]=='#')
{
a1x=i;a2x=i;a1y=j;a2y=j;
if(bfs(i,j)==(a2x-a1x+1)*(a2y-a1y+1))
{
ans++;
}
else
{
flag=0;
break;
}
}
}
if(flag==0)break;
}
if(flag)
{
printf("There are %d ships.",ans);
}
else
{
printf("Bad placement.");
}
}
}
我的另一篇文章
对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
操作:不断枚举入度为0的点入队更新入度。
P1347 排序
#include
#define intn long long
using namespace std;
queue<pair<int ,int> >q;
vector<int >g[50];
int ind[50];
int indz[50];
int main()
{
int n,m;
char a,t,b;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>a>>t>>b;
q.push(make_pair(a-'A'+1,b-'A'+1));//存储m组排序序
}
for(int k=1;k<=m;k++)//枚举排序
{
int ind0_num=0;
int flag=1;
pair<int,int> now=q.front();q.pop();
g[now.first].push_back(now.second);//存边
indz[now.second]++;//每次 进行更新入度
queue<int>qq;
for(int i=1;i<=n;i++)
{
ind[i]=indz[i];//使用更新后的入度
if(ind[i]==0)
{
qq.push(i);// 将入度为零的点入队
ind0_num++;//找到入度为零的点的个数,如果多于1,说明有些关系没有确定
}
}
if(ind0_num!=1)
{
flag=0;
}
int cnt=0;
int ans[30];
while(!qq.empty())//拓扑排序
{
int a=qq.front();
qq.pop();
ans[++cnt]=a;//存储答案
ind0_num=0;
for(int i=0;i<g[a].size();i++)
{
ind[g[a][i]]--;//更新入度
if(ind[g[a][i]]==0)
{
qq.push(g[a][i]);//入队
ind0_num++;
}
}
if(ind0_num>1)
{
flag=0;
}
}
if(cnt!=n)//如果cnt!=n,说明有环
{
printf("Inconsistency found after %d relations.",k);
return 0;
}
else
{
if(flag==0)//如果flag=0,说明有点的排序未确定
{
continue;
}
else
{
printf("Sorted sequence determined after %d relations: ",k);
for(int i=1;i<=n;i++)
{
printf("%c",ans[i]+'A'-1);
}
printf(".");
return 0;
}
}
}
printf("Sorted sequence cannot be determined.");
}
并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。
#include
using namespace std;
int i,j,k,n,m,s,ans,f[10010],p1,p2,p3;
//f[i]表示i的集合名
int find(int k){
//路径压缩
if(f[k]==k)return k;
return f[k]=find(f[k]);
}
int main()
{
cin>>n>>m;
for(i=1;i<=n;i++)
f[i]=i;//初始化i的老大为自己
for(i=1;i<=m;i++){
cin>>p1>>p2>>p3;
if(p1==1)
f[find(p2)]=find(p3);
//p3打赢了p2
else
if(find(p2)==find(p3))
//是否是一伙的
printf("Y\n");
else
printf("N\n");
}
return 0;
}
P2024 [NOI2001]食物链
参考文章
食物链
这道题是要求维护x吃y,y吃z,z吃x的关系,那么我们将并查集扩大三倍,表示三个族群,则可以得到相互的关系(x,y)同类,(x,y+n)x吃y,(x+n,y+2n),x吃y,(x+2n,y)x吃y
int f[150005];
int find(int x)
{
if(f[x]==x)return x;
else
return f[x]=find(f[x]);
}
void merge(int x,int y)
{
int fx=find(x);
int fy=find(y);
f[fx]=fy;
}
main(void)
{
int n,k,jia=0;
int x,y,cmd;
cin>>n>>k;
int p=3*n;
for(int i=1;i<=3*n;i++)f[i]=i;
_1for(i,k)
{
cin>>cmd>>x>>y;
if(x>n||y>n)jia++;
else if(cmd==2&&(x==y||find(x)==find(y)||find(y)==find(x+n)))jia++;
else if(cmd==1&&(find(x)==find(y+n))||(find(y)==find(x+n)))jia++;
else
{
if(cmd==1)
{
merge(find(x),find(y));
merge(find(x+n),find(y+n));
merge(find(x+2*n),find(y+2*n));
}else
{
merge(find(x),find(y+n));
merge(find(x+n),find(y+2*n));
merge(find(x+2*n),find(y));
}
}
}
cout<<jia;
}
带权并查集是a与f[a]直接存在某种可以运算的关系的并查集。
上个题也可以用带权并查集来解决,0表示同类,1表示吃,2表示被吃,进行运算取模可以简便的进行关系的运算。
const int maxn=50010;
int f[maxn];
int ref[maxn];//0同类 1:吃:2被吃
int find(int x)
{
if(f[x]==x)return x;//寻找祖先过程中维护与祖先的关系
int t=f[x];
f[x]=find(f[x]);
ref[x]=(ref[x]+ref[t])%3;
return f[x];
}
void merge(int x,int y,int re)
{
f[x]=y;
ref[x]=re;
}
main(void)
{
int n,k,jia=0;
cin>>n>>k;
_1for(i,n)f[i]=i;
_0for(p,k)
{
int re,x,y;
cin>>re>>x>>y;
if(x>n||y>n)jia++;
else if(re==2&&x==y)jia++;
else if(find(x)==find(y))//已经有了关系,检查关系是否冲突
{
if(re-1!=(ref[x]-ref[y]+3)%3)jia++;
}
else
{
int newre=(re-1+ref[y]-ref[x]+3)%3;
merge(find(x),find(y),newre);
}
}
cout<<jia;
}
经典题目 银河英雄传说
经过思考终于自己做出了这个带权并查集的题目
题意是将30000列舰队进行排列,如M,1,2是将1所在列排在2所在列的后边,要求输出两个舰队之间有几个舰队。
分析:很显然是一个并查集的题目,与一般并查集不同,要求输出一a,b的数值关系。很容易想到,加一个数组维护x与祖先的距离关系,写在find函数里。如寻找x的祖先时,即舰队队列的最前一个元素时,有x-y-z,x的祖先变为z,那么就要更新x与祖先距离nf[x]为原先到y的加上y到z的。
在建立关系时,如将a所在队列连接到b所在队列,一般是将a的祖先连接到b的祖先,但是nf数组还没有更新,nf[a],nf[b]在寻找a-x和b-y时已经更新的,而nf[x]一开始是0,连接y以后是y所在队列的数量所以要加一个数组num[i]表示i所在队列的舰队数量,nf[x]=num[y],因为每次都会找到祖先元素,所以只更新num[y]就可以了。
#include
#define intn long long
int f[30005];
int nf[30005];
int num[30005];
int find(int x)
{
if(f[x]==x)return x;
int t=f[x];
f[x]=find(f[x]);
nf[x]=nf[x]+nf[t];
return f[x];
}
main(void)
{
int t,a,b;
char cmd;
cin>>t;
_1for(i,30000)f[i]=i,num[i]=1;
_1for(p,t)
{
cin>>cmd>>a>>b;
int x=find(a);
int y=find(b);
if(cmd=='M')
{
f[x]=y;
nf[x]=num[y];
num[y]=num[y]+num[x];
}
else
{
if(x!=y)
printf("-1\n");
else
{
printf("%d\n",abs(nf[a]-nf[b])-1);
}
}
// debug(y);debug(nf[y]);debug(num[y]);
}
}
缩点
tarjan算法
void tarjan(int x)
{
dfn[x]=low[x]=++num;
ins[x]=1;
st.push(x);
for(int i=0;i<g[x].size();i++)
{
int q=g[x][i];
if(dfn[q]==0)
{
tarjan(q);
low[x]=min(low[q],low[x]);
}
else if(ins[q]==1)
{
low[x]=min(low[x],dfn[q]);
}
}
if(dfn[x]==low[x])
{
numb++;
int p;
do
{
p=st.top();
st.pop();
bl[p]=numb;
nums[numb]++;
ins[p]=0;
}
while(x!=p);
}
}
使用邻接矩阵,一开始将mp初始化为inf,然后更新路径,从某一点prime函数,更新这一点的dist,进入for循环, 只需要n-1次循环,寻找最小dist,加入最短路径的点后更新dist。最终得到结果。
const int inf=999999;
int mp[400][400];
int n;
int visit[400];
int dist[400];
int ans=0;
int prime(int cur)
{
int index;
int sum=0;
visit[cur]=1;
_1for(i,n)dist[i]=mp[cur][i];
for(int i=1;i<=n-1;i++)
{
int mincost=inf;
_1for(j,n)
{
if(!visit[j]&&dist[j]<mincost)
{
mincost=dist[j];
index=j;
}
}
debug(index);
visit[index]=1;
sum+=mincost;
ans=max(ans,mincost);
_1for(j,n)
{
if(!visit[j]&&dist[j]>mp[index][j])
{
dist[j]=mp[index][j];
}
}
}
return sum;
}
main(void)
{
int m,x,y,z;
cin>>n>>m;
_1for(i,n)
_1for(j,n)
mp[i][j]=inf;
_1for(i,m)
{
cin>>x>>y>>z;
mp[x][y]=z;
mp[y][x]=z;
}
prime(1);
cout<<n-1<<" "<<ans;
}
需要用到并查集,使用vector存图,将路径从小到大排列 ,从1开始枚举路径,如果路径的点没有经过,加一条,更新并查集,直到加了n-1条边为止。
int f[1000];
int ans=0;
int ansm=0;
struct node
{
int u,v,w;
}a[100005];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(f[x]==x)return x;
return f[x]=find(f[x]);
}
main(void)
{
int n,m;
cin>>n>>m;
_1for(i,n)f[i]=i;
_1for(i,m)
{
cin>>a[i].u>>a[i].v>>a[i].w;
}
sort(a+1,a+1+m,cmp);
int cnt=0;
_1for(i,m)
{
if(cnt==n-1)break;
int x=find(a[i].u);
int y=find(a[i].v);
if(x!=y)
{
f[x]=y;
ans+=a[i].w;
cnt++;
ansm=max(ansm,a[i].w);
}
}
cout<<n-1<<" "<<ansm;
}