【BFS】一本通提高篇

1448:【例题1】电路维修

这道题难在建图这里
我们可以把电路板上的每个格点(横线与竖线的交叉点)看作无向图中的结点。
若两个结点x和y是某个小方格的两个对角,则在x与y之间连边。若该方格中的标准件(对角线)与x到y的线段重合,则边权为0;若垂直相交,则边权为1(说明需要旋转1次才能连通)。然后,
我们在这个无向图中求出从左上角到右下角的最短距离,就得到了结果。

这是一个边权要么是0,要么是1的无向图。在这样的图上,我们可以通过双端队列广度搜索计算。
分支边权为1,从队尾入
分支边权为0,从队首入

保证两段性和单调性

//!!!!if((r+c)%2) 那么无解
这个双端队列和平时写的不一样,是从中点分开的向两边扩展

还是这个讲的比较好https://www.cnblogs.com/xsl19/p/12380226.html

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3f3f3f3f;
typedef long long LL;
typedef unsigned long long ull;
//还是这个讲的比较好https://www.cnblogs.com/xsl19/p/12380226.html 
const int M=1e6+10;
int dis[M],vis[M];
int n,m,t,tot;
int head[M];
char mp[510][510];
struct node{
	int to,w,nex;
}ed[M<<1];
void adde(int x,int y,int w){
	ed[++tot].to=y;
	ed[tot].w=w;
	ed[tot].nex=head[x];
	head[x]=tot;
}
int id(int x,int y){
	return (x)*(m+1)+y;
}
void bfs(int x){
	memset(vis,0,sizeof(vis));
	memset(dis,INF,sizeof(dis));
	dis[0]=0;
	deque<int> q; //双端队列 
	q.push_back(x);
	while(!q.empty()){
		int u=q.front();
		q.pop_front();
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=head[u];i;i=ed[i].nex){
			int tt=ed[i].to;
			if(vis[tt]) continue;
			if(dis[tt]>dis[u]+ed[i].w){
				dis[tt]=dis[u]+ed[i].w;
				if(!ed[i].w) q.push_front(tt);  //如果是正向的话,就插在队首
				else q.push_back(tt); 
			}
		}
	}
}
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d %d",&n,&m);
		tot=0;
		memset(head,0,sizeof(head));
		for(int i=1;i<=n;i++) scanf("%s",mp[i]+1);
		if((n+m)%2) {
			printf("NO SOLUTION\n");
			continue;
		} 
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++){
				if(mp[i][j]=='\\'){
					adde(id(i-1,j-1),id(i,j),0); //起点--终点原本的方向 
					adde(id(i,j),id(i-1,j-1),0); //终点--起点原本的方向
					adde(id(i,j-1),id(i-1,j),1); //终点--起点  反向
					adde(id(i-1,j),id(i,j-1),1); //起点--终点  反向 
				}
				else{
					adde(id(i,j-1),id(i-1,j),0);
					adde(id(i-1,j),id(i,j-1),0);
					adde(id(i,j),id(i-1,j-1),1);
					adde(id(i-1,j-1),id(i,j),1);
				}
			}
		}
		bfs(0);
		printf("%d\n",dis[id(n,m)]);
	}
return 0;
}

  

1491 -- 【搜索优化】魔板(USACO3.2.5)2117

这道题地重点就在于写函数:对状态进行改变,运用map高校实现状态记录+判重+记录路径地效果

而且记录路径!

map<string,string> mp; //去重+记录路径
//广搜+map去重 可以先把它当字符串处理,再找出ABC三种操作的规律,用函数模拟。map里存操作序列
queue<string> q;
string a;
void A(string x){  //调换上下两行 
	string xx=x;
	for(int i=0;i<4;i++){
		char x1=x[i];
		x[i]=x[7-i];
		x[7-i]=x1;
	}
	//把x进行变换 
	if(mp.count(x)==0){  //并且没有出现过 
		q.push(x);
		mp[x]=mp[xx]+'A'; 
	}
	return;
} 
void B(string x){ //将最右边的一行插入最左边; 
	string xx=x;
	 x[0]=xx[3],x[1]=xx[0],x[2]=xx[1],x[3]=xx[2],x[4]=xx[5],x[5]=xx[6],x[6]=xx[7],x[7]=xx[4];
	if(mp.count(x)==0){
		q.push(x);
		mp[x]=mp[xx]+'B';
	}
	return;
}
void C(string x){
	string xx=x;
	x[1]=xx[6];
	x[2]=xx[1];x[5]=xx[2];x[6]=xx[5];
	if(mp.count(x)==0){
		q.push(x);
		mp[x]=mp[xx]+'C'; 
	}
	return;
}
void bfs(){
	q.push("12345678");
	mp["12345678"]=""; //路径初始化
	while(!q.empty()){
		A(q.front());
		B(q.front());
		C(q.front());
		if(mp.count(a)!=0){
			cout<<mp[a].size();
			cout<<endl<<mp[a]<<endl;
			return;
		}
		q.pop();
	} 
	return;
}


int main(){
	char x;
	for(int i=0;i<8;i++){
		cin>>x;
		a=a+x;
		getchar();
	}
	bfs();
return 0;
}

  

1492 -- 【搜索优化】Knight Moves(PKU1915)2894

双向广搜的例题

对两个方向的搜索分别标记不同的标志,例题从起点出发的标记为vis[]=1,从终点出发的标记为vis[]=2

//双向广搜
//要标记正反向的搜索记录 
int n,sx,sy,ex,ey;
int dis[8][2]={{1,2},{-1,2},{1,-2},{-1,-2},{2,1},{-2,-1},{-2,1},{2,-1}};
int vis[maxn][maxn]; //正向是1,逆向是2
int step[maxn][maxn]; 
int xx[1000000],yy[1000000]; //数组模拟队列 
int fun(){
	memset(step,0,sizeof(step));
	memset(vis,0,sizeof(vis));
	int head=0,tail=0;
	xx[tail]=sx;yy[tail++]=sy;vis[sx][sy]=1;
	xx[tail]=ex;yy[tail++]=ey;vis[ex][ey]=2;
	while(head!=tail){
		int x=xx[head];
		int y=yy[head++];
		int t=step[x][y];
		for(int i=0;i<8;i++){
			int l=x+dis[i][0],r=y+dis[i][1];
			if(l<0||l>=n||r<0||r>=n) continue;
			if(vis[l][r]!=vis[x][y]&&vis[l][r]&&vis[x][y]) 
			return step[l][r]+step[x][y]+1; //有了交会 
			if(!vis[l][r]){
				xx[tail]=l;
				yy[tail++]=r;
				step[l][r]=t+1;
				vis[l][r]=vis[x][y];
			}
		}
	}
	return 0;	
}



int main(){

	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%d %d %d %d %d",&n,&sx,&sy,&ex,&ey);
		printf("%d\n",fun());
	}
	
	
	
return 0;
}

1493 -- 【搜索优化】棋盘游戏

这道题是运用BFS+位运算,因为表示状态为16位的01串,所以用二进制来压缩状态

ps:x,w=1<<i,z=1<<(i-1)   x^w^z就是交换第i位和第j位的状态

另外:输入格式不要想的太难

//BFS和位运算
struct node{
	int num; //当前的状态:压缩
	int step; //到这里的步数 
};
queue<node> q;
int aa,bb; //分别是起始和终点状态 
int vis[100000];
void bfs(){
	q.push((node){aa,0});
	while(!q.empty()){
		node head=q.front();
		q.pop();
		if(head.num==bb){
			cout<<head.step<<endl;
			return;
		}
		int tem=head.num;
		for(int i=15;i>=0;i--){
			int x=(15-i)/4;//行
			int y=(15-i)%4; //列
			int w=1<<i; 
			if(y<3&&(tem&(1<<i))!=(tem&(1<<i-1))){
				//左右两边不同,可以交换
				int z=1<<i-1; 
				if(!vis[tem^z^w]){
					//其实这样就算两个位值交换了
					vis[tem^z^w]=1;
					q.push((node){tem^z^w,head.step+1}); 
				}
			}
			if(x<3&&(tem&(1<<i))!=(tem&(1<<i-4))){
				int z=1<<i-4;
				if(!vis[tem^w^z]){
					vis[tem^w^z]=1;
					q.push((node){tem^w^z,head.step+1});
				}
			} 
		}
	}
}

 
int main(){
	char c;
	//我还以为这个格式很难控制。。。。学的不够 
	for(int i=15;i>=0;i--){
		cin>>c;
		if(c!='0') aa+=1<<i;
	}
	for(int i=15;i>=0;i--){
		cin>>c;
		if(c!='0') bb+=1<<i;
	}
	if(aa==bb) cout<<"0"<<endl;
	else bfs();
	
	
return 0;
}

  

1452:Keyboarding

出自 World Final 2015 F. Keyboarding

给定一个 r 行 c 列的在电视上的“虚拟键盘”,通过「上,下,左,右,选择」共 5 个控制键,你可以移动电视屏幕上的光标来打印文本。一开始,光标在键盘的左上角,每次按方向键,光标总是跳到下一个在该方向上与当前位置不同的字符,若不存在则不移动。每次按选择键,则将光标所在位置的字符打印出来。

现在求打印给定文本(要在结尾打印换行符)的最少按键次数。

很容易想到是 BFS,但直接用 BFS 来会 TLE,因此我们需要进行优化
考虑对后置点进行优化,我们知道,通过 BFS 到达的点,如果之前被访问过,那么肯定之前访问的步数最小,因此在 BFS 的过程中,可以记录打印出的字符的位置,这样一来,
如果一个后置点的位置要小于前置点,那么无论如何距离也不可能更小,这样就不用向下进行扩展了,同时,如果后置点的位置更大,那么就将当前点的位置赋给后置点
原文链接:https://blog.csdn.net/u011815404/article/details/100811183
其实就是最优化剪枝:如果搜索到的点在之前已经搜索到了并且那一次搜索时匹配的字符串长度大于本次的长度,剪枝。(最优性剪枝)
下一步走到的点先进行预处理,方便广搜。

剪枝操作:

if(vis[nx][ny]<opp){
vis[nx][ny]=opp; //这里进行了剪枝

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=55; 
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
struct node{
	int x,y;
	int op,step;
	///选择次数,按键次数
	node(){}
	node(int x,int y,int op,int step):x(x),y(y),op(op),step(step){}
};
int n,m;
//每个点的移动对象 
struct nex{
	int x,y;
	nex(){}
	nex(int x,int y):x(x),y(y){} 
}nxt[maxn][maxn][4];
char str[maxn][maxn],s[10005];
int g[maxn][maxn],vis[maxn][maxn];
void bfs(int len){
	queue<node> q;
	memset(vis,-1,sizeof(vis));
	q.push(node(1,1,0,0));
	while(!q.empty()){
		node top=q.front();
		q.pop();
		int xx=top.x,yy=top.y,opp=top.op,steps=top.step;
		if(str[xx][yy]==s[opp+1]){
			q.push(node(xx,yy,opp+1,steps+1)); //这里如果是正确的话,opp这个值会加1,然后就会逐步形成最优 
			if(opp+1==len){
				printf("%d\n",steps+1);
				return;
			}
		}
		else{
			for(int i=0;i<4;i++){
				//这里就需要main里面预处理 
				int nx=nxt[xx][yy][i].x,ny=nxt[xx][yy][i].y;
				if(nx==0) continue; //超出范围
				if(vis[nx][ny]<opp){
					vis[nx][ny]=opp; //这里进行了剪枝 
					q.push(node(nx,ny,opp,steps+1));
				} 
			}
		}
	}
}
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%s",str[i]+1);
	scanf("%s",s+1);
	int len=1;
	while(s[len]) len++; //这个判断条件就可以了
	s[len]='*'; //表示最后还需要一个断行
	//预处理能走的地方 
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			for(int k=j-1;k>=1;k--){ //向上 
				if(str[i][j]!=str[i][k]){ //光标总是跳到下一个在该方向上与当前位置不同的字符
					nxt[i][j][0]=nex(i,k);
					break;
				}
			}
			for(int k=j+1;k<=m;k++){ //向下 
				if(str[i][j]!=str[i][k]){
					nxt[i][j][1]=nex(i,k);
					break;
				}
			}
			for(int k=i-1;k>=1;k--){  //向左 
				if(str[i][j]!=str[k][j]){
					nxt[i][j][2]=nex(k,j);
					break;
				}
			}
			for(int k=i+1;k<=n;k++){
				if(str[i][j]!=str[k][j]){
					nxt[i][j][3]=nex(k,j);
					break;
				}
			}
		}
	}
	bfs(len);
	
return 0;
}

  

1453:移动玩具

和棋盘游戏一样的,注意二进制的使用方法

如果只是单纯从起点开始搜索:注意操作temp^x^y,其实就是把x和y两个位置的数字交换了

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
//BFS和位运算
//其实位运算更难 
struct node{
	int num; //当前的状态:压缩
	int step; //到这里的步数 
};
queue<node> q;
int aa,bb; //分别是起始和终点状态 
int vis[100000];
void bfs(){
	q.push((node){aa,0});
	while(!q.empty()){
		node head=q.front();
		q.pop();
		if(head.num==bb){
			cout<<head.step<<endl;
			return;
		}
		int tem=head.num;
		for(int i=15;i>=0;i--){
			int x=(15-i)/4;//行
			int y=(15-i)%4; //列
			int w=1<<i; 
			if(y<3&&(tem&(1<<i))!=(tem&(1<<i-1))){
				//左右两边不同,可以交换
				int z=1<<i-1; 
				if(!vis[tem^z^w]){
					//其实这样就算两个位值交换了
					vis[tem^z^w]=1;
					q.push((node){tem^z^w,head.step+1}); 
				}
			}
			if(x<3&&(tem&(1<<i))!=(tem&(1<<i-4))){
				int z=1<<i-4;
				if(!vis[tem^w^z]){
					vis[tem^w^z]=1;
					q.push((node){tem^w^z,head.step+1});
				}
			} 
		}
	}
}

 
int main(){
	char c;
	//我还以为这个格式很难控制。。。。学的不够 
	for(int i=15;i>=0;i--){
		cin>>c;
		if(c!='0') aa+=1<<i;
	}
	for(int i=15;i>=0;i--){
		cin>>c;
		if(c!='0') bb+=1<<i;
	}
	if(aa==bb) cout<<"0"<<endl;
	else bfs();
	
	
return 0;
}

  如果用双向广搜,记得标记方向

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=(1<<16)+10;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
//这种题和八数码也蛮像的
//重点是状态表达---状态转移(bfs
//而且这种01表达的就自然很能想到二进制,所以二进制的操作要记得 
//可以用双向广搜
int vis[2][maxn]; //不仅可以用来判重,还可以用来计算步数 
int que[2][maxn],s,e;
void inti(){
	char x;
	for(int i=1;i<=16;i++){
		cin>>x; //可以直接这样用,也可以用getchar
		s=(s<<1)+(x-'0'); 
	}
	for(int i=1;i<=16;i++){
		cin>>x;
		e=(e<<1)+(x-'0');
	}
}
int flag,now,u,head[2],tail[2];
void expand(int des,int b){ //b是哪个方向的移动,des是目标 
	if(now&(1<<des)) return;  //如果当前位置有玩具了
	int k=now+(1<<des);
	if(vis[b][k]) return;
	if(vis[b^1][k]) {
		printf("%d\n",vis[b^1][k]+vis[b][u]-1); //如果已经走到了这里来了,就需要相加-1
		flag=1;
		return; 
	} 
	vis[b][k]=vis[b][u]+1;
	que[b][++tail[b]]=k; //入队 
}
void bfs(){
	head[0]=head[1]=tail[0]=tail[1]=1;
	que[0][head[0]]=s;
	que[1][head[1]]=e;
	vis[0][s]=1;
	vis[1][e]=1;
	if(s==e){
		printf("0\n");
		return;
	}
	while(head[0]<=tail[0]&&head[1]<=tail[1]){
		u=que[0][head[0]++];
		for(int i=15;i>=0;i--){
			if(!u&&(1<<i)) continue; //这里没有玩具
			now=u^(1<<i); //让他这里变得没有玩具
			if(i/4<3) expand(i+4,0);
			if(flag) return;
			if(i/4>0) expand(i-4,0);
			if(flag) return;
			if(i%4!=3) expand(i+1,0);
			if(flag) return;
			if(i%4!=0) expand(i-1,0);
			if(flag) return;
		}
		u=que[1][head[1]++];
		for(int i=15;i>=0;i--){
			if(!u&&(1<<i)) continue; //这里没有玩具
			now=u^(1<<i); //让他这里变得没有玩具
			if(i/4<3) expand(i+4,1);
			if(flag) return;
			if(i/4>0) expand(i-4,1);
			if(flag) return;
			if(i%4!=3) expand(i+1,1);
			if(flag) return;
			if(i%4!=0) expand(i-1,1);
			if(flag) return;
		}
	}
}
int main(){
	inti();
	bfs();
return 0;
}

#include<bits/stdc++.h>

using namespace std;

#define maxn 20
#define maxm (1 << 16) + 5
#define file(a) freopen(#a".in","r",stdin); freopen(#a".out","w",stdout);
typedef long long ll;

int v[2][maxm], q[2][maxm];
int s, e, h[2], t[2], now, u, k, f;
char c;

int read(){
	char orz; int tql = 0, qwq = 1;
	orz = getchar();
	while(orz > '9' || orz < '0'){
		if(orz == '-') qwq = -1;
		orz = getchar();
	}
	while(orz >= '0' && orz <= '9'){
		tql = (tql << 3) + (tql << 1) + orz - '0';
		orz = getchar();
	}
	return tql * qwq;
}

void init(){
	for(int i = 1; i <= 16; i++){
		cin >> c;//getchar会更快
		s = (s << 1) + c - '0';
	}
	for(int i = 1; i <= 16; i++){
		cin >> c;
		e = (e << 1) + c - '0';
	}
}

void expand(int m, int b){
	if(now & (1 << m)) return;//要换的位置有玩具 
	int k = now + (1 << m);//换后的棋盘 
	if(v[b][k]) return;//判重 
	if(v[b ^ 1][k]){//对方搜过则直接输出 
		printf("%d", v[b ^ 1][k] + v[b][u] - 1);
		f = 1;
		return;
	}
	v[b][k] = v[b][u] + 1;//步数+1 
	q[b][++t[b]] = k;//入队 
}

void bfs(){
	h[0] = t[0] = h[1] = t[1] = 1;
	q[0][h[0]] = s; v[0][s] = true;
	q[1][h[1]] = e; v[1][e] = true;
	if(s == e){
		printf("0");
		return;
	}//特判一下 
	while(h[0] <= t[0] && h[1] <= t[1]){
		u = q[0][h[0]++];
		for(int i = 15; i >= 0; i--){
			if(! (u & (1 << i))) continue;//当前位木有玩具 
			now =  u ^ (1 << i);//当前位有玩具变没玩具,没玩具变有玩具 
			if(i / 4 < 3) expand(i + 4, 0); if(f) return;//最后一行不可以向下移 
			if(i / 4 > 0) expand(i - 4, 0); if(f) return;//第一行不可以向上移 
			if(i % 4 != 3) expand(i + 1, 0); if(f) return;//最后一列不可向右移 
			if(i % 4 != 0) expand(i - 1, 0);  if(f) return;//第一列不可以左移 
		}
		u = q[1][h[1]++];
		for(int i = 15; i >= 0; i--){
			if(! (u & (1 << i))) continue;
			now =  u ^ (1 << i);
			if(i / 4 < 3) expand(i + 4, 1); if(f) return;
			if(i / 4 > 0) expand(i - 4, 1); if(f) return;
			if(i % 4 != 3) expand(i + 1, 1); if(f) return;
			if(i % 4 != 0) expand(i - 1, 1);  if(f) return;
		}
	}
}

int main(){
	//file(date);
	init();
	bfs();
	return 0;
}

  

1454:山峰和山谷

广搜的剪枝 ,每个点进行广搜,每搜完一个点,把与它相连接的点标记,不重复的搜。每个点之搜索依次,
特别注意,如果整个地图方格的高度均相同,则整个地图既是一个山谷,也是一个山峰。

tips:判断是山峰还是山谷(flag1,flag2),这个连通块里面有多少个点(判断整张图)

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
//广搜的剪枝 ,每个点进行广搜,每搜完一个点,把与它相连接的点标记,不重复的搜。
//特别注意,如果整个地图方格的高度均相同,则整个地图既是一个山谷,也是一个山峰。
int mp[maxn][maxn];
int n,high,low,cnt,vis[maxn][maxn];
int dis[10][2]={0,1,0,-1,1,0,-1,0,1,-1,-1,-1,-1,1,1,1}; //md这个写错了!! 
struct node{
	int x,y;
	//node(int xx,int yy) x(xx),y(yy){};
};
void BFS(int x1,int y1){
	bool flag1=0,flag2=0;
	int cnt=1;
	vis[x1][y1]=1;
	queue<node> q;
	node now,pre;
	pre.x=x1;
	pre.y=y1;
	q.push(pre);
	while(!q.empty()){
		now=q.front();
		q.pop();
		for(int i=0;i<8;i++){
			pre.x=now.x+dis[i][0];
			pre.y=now.y+dis[i][1];
		
			if(pre.x>n||pre.y>n||pre.x<=0||pre.y<=0||(vis[pre.x][pre.y]&&mp[pre.x][pre.y]==mp[now.x][now.y]))
//标记已走过的坐标,且改坐标对应的值与之前的值相同
				continue;
			else if(mp[pre.x][pre.y]==mp[now.x][now.y]){
					q.push(pre);
					vis[pre.x][pre.y]=true;
					cnt++;
					//cout<<cnt<<endl;
			}
			else{
				if(mp[now.x][now.y]>mp[pre.x][pre.y])
					flag1=true;
				else
					flag2=true;
			}
		}
	}
	if(flag1&&flag2) return;
	else if(flag1) high++;
	else if(flag2) low++;
	if(cnt==n*n) {
		low++;high++;
	}
	return;
}
int main(){
	scanf("%d",&n);
	low=0;high=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cin>>mp[i][j];
		}
	}
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(!vis[i][j]) {
			//	cout<<"heihei "<<i<<j<<endl;
				BFS(i,j);
				
			}
		}
	}
	printf("%d %d",high,low);
return 0;
}

  

【DFS】--一本通提高篇

优化技巧:

1、优化搜索顺序

2、可行性剪枝(上下界剪枝

3、最优性剪枝(已经不满足求出来的最优值了)

4、记忆化

5、排除等效冗余

1440:【例题1】数的划分(可行性剪枝,上下界剪枝)

类型
DFS 可行性剪枝 上下界剪枝
题解
为了防止TLE,那就不能简单暴搜
1.由于分解数字不考虑顺序,我们不如设定分解的数字依次递增,所以扩展节点时的下界>=前一个结点的值,也就是a[ i ] >= a[ i-1 ]
2.那么上界呢?假设已经分解出了k份,那么 i + 1 份不超过 n' /(m-k+1)
因为你已经分解了k份,还有m-k+1份待分解,但是这 m-k+1份的和为 n' ,因为始终保证分解出的序列非递减,那么,第k份最大就是平均数

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
/*
类型
DFS  可行性剪枝  上下界剪枝
题解
为了防止TLE,那就不能简单暴搜
1.由于分解数字不考虑顺序,我们不如设定分解的数字依次递增,所以扩展节点时的下界>=前一个结点的值,也就是a[ i ] >= a[ i-1 ]
2.那么上界呢?假设已经分解出了k份,那么 i + 1 份不超过 n' /(m-k+1) 
   因为你已经分解了k份,还有m-k+1份待分解,但是这 m-k+1份的和为 n' ,因为始终保证分解出的序列非递减,那么,第k份最大就是平均数
*/
/*
dp的写法
cin>>k>>n;
	//k是总数,n是个数 
	a[0][0]=1;
	for(int i=1;i<=n;i++){
		for(int j=i;j<=k;j++){//这个是没有0的,所以j的下标有限制 
			a[i][j]=a[i-1][j-1]+a[i][j-i];
		}
	}
	cout<<"no zero is ";
	cout<<a[n][k]<<endl; 
*/ 
int n,m,ans;
int a[10];
void dfs(int k){ //分第k份 
	if(n==0) return;
	if(k==m) {
		if(n>=a[k-1]){  //最后剩下的n,保证大于已经算到的最后一位,也就是保证递增,不会出现重复 
			ans++;
			for(int i=1;i<m;i++) printf("%d ",a[i]);
			printf("%d\n",n);
			//printf("\n"); 
			return;
		}
	}
	for(int i=a[k-1];i<=n/(m-k+1);i++){//第K份的上下界 
		a[k]=i; //第k份的值
		n-=i;
		dfs(k+1);
		n+=i; //回溯 
		
	}
}
int main(){
	cin>>n>>m;
	a[0]=1;  //是为了控制第一位i的下标,从1开始 
	dfs(1);
	printf("%d\n",ans);
return 0;
}

  

1446 -- 【搜索练习】数独

输入数据一共9行,每行有9个字符。
输入数据描述了一个待解决的数独,其中,“?”表示数独中的空缺。
我们的输入数据总保证有唯一解。输出一共9行,每行9个数字,表示你的答案。

需要设置3个判断条件,这一行这个数据有没有使用过,这一列这个数据有没有使用过,这个小9宫格这个数据有没有使用过,如果没有就用,然后判断是否可以退出或者怎样进行下一步,递归之后进行回溯,修改回原来的情况

#include<cstdio>
#include<cstring>
#include<cstdlib>
int a[10][10];
bool h[10][10],l[10][10],g[10][10];//行,列,第几个格子
void print()//输出函数 
{
    for(int i=1;i<=9;i++)
    {
        for(int j=1;j<=9;j++)
            printf("%d",a[i][j]);
        if(i!=9) printf("\n");
    }
    exit(0);
}
void dfs(int x,int y)//深搜 
{
    if(a[x][y]!=0)//9*9中不为零的数直接跳过 
    {
        if(x==9&&y==9) 
            print();//搜索结束后输出 
        if(y==9) //行到顶端后搜索列 
            dfs(x+1,1); 
        else //搜索行 
            dfs(x,y+1);
    }
    if(a[x][y]==0)//等于零时 
    {
        for(int i=1;i<=9;i++)
        { 
            if(h[x][i]&&l[y][i]&&g[(x-1)/3*3+(y-1)/3+1][i])
            {
                a[x][y]=i;
                h[x][i]=false;//此格被占 (行) 
                l[y][i]=false;//同上 (列) 
                g[(x-1)/3*3+(y-1)/3+1][i]=false;//同上 (格子) 
                if(x==9&&y==9) //同a[x][y]!=0时                    
                    print();
                if(y==9) dfs(x+1,1); else dfs(x,y+1);
                a[x][y]=0;//当前格退出 
                h[x][i]=true;
                l[y][i]=true;
                g[(x-1)/3*3+(y-1)/3+1][i]=true;
            }
        } 
    }
}
int main()
{
	char s[20];
    memset(h,true,sizeof(h));
    memset(l,true,sizeof(l));
    memset(g,true,sizeof(g));
    for(int i=1;i<=9;i++)
    {
    	scanf("%s",s+1);
        for(int j=1;j<=9;j++)
        {
            
            if(s[j]>='0'&&s[j]<='9')
            {
            	a[i][j]=s[j]-'0';
                h[i][a[i][j]]=false;//表示格子上有数 
                l[j][a[i][j]]=false;//同上 
                g[(i-1)/3*3+(j-1)/3+1][a[i][j]]=false;//同上 
            }
            else a[i][j]=0;
        }
    } 
    dfs(1,1);
    return 0;
}

  

1488 -- 【搜索优化】生日蛋糕1886(上下界剪枝、优化搜索顺序、可行性剪枝、最优性剪枝)

一个剪枝地优秀应用

//生日蛋糕:这道题是DFS+剪枝,
//但是一开始完全没思路,虽然知道是DFS,但其实剪枝能够想到地也有很多:
//1、如果当前(体积)加上已有地体积)大于应该的面积、体积,就退出
//2\当前的奶油面积+之后的最小奶油面积>现在已求出的的最小奶油面积——果断return
//2、如果当前体积>n,return
//3、下标优化:从上一个半径和高+1开始
int a[30],b[30]; //表面积,体积 
//
int n,m,ans;
void dfs(int v,int s,int p,int r,int h){
//分别是:v已用体积,s已有表面积,p剩余层数,r半径,h高
	if(p==0){
		if(v==n&&s<ans) ans=s;
		return;
	}	
	if(v+b[p-1]>n) return; //体积超出了
	if(2*(n-v)/r+s>=ans) return; //表面积超出了最优值
	//当前的表面积+余下的侧面积>当前最优值
	for(int i=r-1;i>=p;i--){ //递减的半径 
		if(p==m) s=i*i; //最后一层了就直接保存
		int hh=min(h-1,(n-v-b[p-1])/(i*i)); //能娶到的最大高度
		for(int j=hh;j>=p;j--){  //递减的高度 
			dfs(v+i*i*j,s+2*i*j,p-1,i,j);
			//体积      表面积 
		} 
	} 
}
int main(){
	cin>>n>>m;
	ans=INF;
	a[0]=b[0]=0;
	for(int i=1;i<21;i++){
		b[i]=b[i-1]+i*i*i; ////第i层使用的最大体积 这个也要初始化! 
	} 
	dfs(0,0,m,n+1,n+1); 
	if(ans==INF) cout<<"0"<<endl;
	else cout<<ans<<endl;
return 0;
}

  

1489 -- 【搜索优化】小木棍(PKU1011)1461(最优性剪枝,可行性剪枝)

也是剪枝+深搜地优秀应用

int n,x,len;
//https://www.luogu.com.cn/problemnew/solution/UVA307
int aa[100]; 
int vis[110]; //这个也要,反正要剪枝,这个是基础步骤 
//我怎么天真的可爱,虽然单纯判断summ%i==0能拿点分,但是我完全没有考虑能不能拼在一起的问题
//所以这道题的方法应该是搜索
//DFS+剪枝(可以算是剪枝的典型题
inline bool cmp(int a,int b){
	return a>b; //木棒的长度从大到小排序,因为先排大的,能够让搜索树减小 
} 
//三个参数:分别是已经拼好的木棍数,当前凑的长度,上一个的编号 

//f1变量的优化:
//fl记录当前木棒最近一次添加的木棍长度。倘若两相邻木棍长度相同,前一根木棍已然匹配失败,
//则再尝试后一根情况一样失败。所以fl就是为了避免两相邻木棍长度相同且重复同样的失败。 
inline bool dfs(int cnt,int lnow,int las){
	if(cnt==x+1) return 1;
	if(lnow==len) return dfs(cnt+1,0,1); //进行下一根棍子的搜索
	int f1=0;
	for(int i=las;i<=n;i++){ //这个下标也是为了控制顺序地进行匹配,如果东西一样顺序不同就是重复了 
		if(!vis[i]&&f1!=aa[i]&&lnow+aa[i]<=len){
			//首先美访问过,与它一样地没有访问失败
			//并且最基本:加起来不超过当前规定地长度
			vis[i]=1;
			if(dfs(cnt,lnow+aa[i],i)) return 1;
			vis[i]=0; //回溯 //还原搜索前的状态。
			f1=aa[i];
			if(len==0) return false;
			//这一步是为了:如果第一根棍子就已经匹配失败,那么揪下来也不用匹配了 
		}
	}
	return false; 
}
int main(){
	cin>>n;
	int summ=0;
	int maxx=-1;
	for(int i=1;i<=n;i++){
		cin>>aa[i];
		summ+=aa[i];
		maxx=max(maxx,aa[i]);
	}
	sort(aa+1,aa+1+n,cmp);
	//的是个全局变量 
	for(len=maxx;len<=summ;len++){ //从最大的长度开始搜索,也是一种策略 
		if(summ%len==0){
			x=summ/len; //必须要达到的木棍数 
			memset(vis,0,sizeof(vis));
			if(dfs(1,0,1)){ //从1开始,所以递归边界是x+1 
				cout<<len;
				break;
			}  
		}
	}
return 0;
}


#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
int n,x,t;
int cnt,sum,val;
int v[105],s[105];

inline bool cmp(int a,int b){return a>b;}
inline bool dfs(int nbr,int len,int las){
    if(nbr>x) return true;
    if(len==t) return dfs(nbr+1,0,1);
    int fl=0;
    for(int i=las;i<=n;i++){
        if(!v[i]&&len+s[i]<=t&&fl!=s[i]){
            v[i]=1;
            if(dfs(nbr,len+s[i],i)) return true;
            v[i]=0,fl=s[i];
            if(len==0) return false;
        }
    }
    return false;
}

int main(){
    while(cin>>n&&n){
        sum=val=0;
        for(int i=1;i<=n;i++){
            cin>>s[i];
            sum+=s[i],val=max(val,s[i]);
        }
        sort(s+1,s+n+1,cmp);
        for(t=val;t<=sum;t++){
            if(sum%t) continue;
            x=sum/t;
            memset(v,0,sizeof(v));
            if(dfs(1,0,1)){
                cout<<t<<endl;
                break;
            }
        }
    }

    return 0;
}

  

1443:【例题4】Addition Chains(优化搜索顺序)

简单的剪枝操作:

(1)超过最小层数,不算了

(2)上一层的数字大于最大值,不能算了

(3)下标:从后面倒着往前加起来,先从大的开始

记得回溯

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=2010;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
//先试试简单的:最优化剪枝 
//有点复杂的,剪枝蛮多的:https://blog.csdn.net/weixin_30767921/article/details/97091862 
int n,minn,ans[maxn],a[maxn];
void dfs(int x){ //x是递归到的层数 
	if(x-1>minn) return ;// jian1:超过最小层数
	if(a[x-1]>n) return; //上一层的数字大于最大值,剪枝
	if(a[x-1]==n){
		if((x-1)>=minn) return; //
		minn=x-1;
		for(int i=1;i<=x;i++) ans[i]=a[i];
	} 
	else{
		for(int j=x-1;j>=1;j--){  //从后面倒着往前加起来,先从大的开始 
			if(a[j]+a[x-1]<=n){
				a[x]=a[x-1]+a[j];
				dfs(x+1);
				a[x]=0; //回溯 
			}
		}
	}
}
int main(){
	while(scanf("%d",&n)&&n){
		a[1]=1;
		minn=INF;
		dfs(2);
		for(int i=1;i<=minn;i++) printf("%d ",ans[i]);
		printf("\n");
	}
return 0;
}

  

居然还有一道题在网站上没有的,我晕,在书上,绕死了

weight(搜索对象的选择,根据序列的特性确定是放在左边还是右边,理解这个思想)

https://blog.csdn.net/qq_42367531/article/details/85276675

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,a[2100],ans[2100];//s数组是我们记录答案序列的数组 
bool bk[210000],flag;//flag表示我们是否找到目标 
void dfs(int k,int x,int y,int sx,int sy)
//k表示我们当前递归搜索的这个数,x表示数列的头,y表示数列的尾
//sx表示的是前缀和序列Si当中的数,sy表示的是后缀和序列Ti当中的数
//sx和sy都是我们要搜索得到的数 
{
	if(flag==true)return;//如果我们找到了,就返回到上一次 
	if(x==y)//如果头等于尾,说明这个递归已经成立 
	{
		if(bk[a[k]-sx]==false && bk[a[k]-sy]==false)return;
		//如果我们需要的条件是不成立的,就返回到上一层 
		if(a[2*n]-sx-sy<1 || a[2*n]-sx-sy>500)return;
		//如果不在这个集合当中的话,就返回到上一层 
		ans[x]=a[2*n]-sx-sy;//更新我们答案序列 
		for(int i=1;i<=n;i++)printf("%d ",ans[i]);//输出答案 
		printf("\n");flag=true;//记录我们已经找到了,方便在上面返回 
	}
	else
	{
		if(bk[a[k]-sx]==true && 1<=a[k]-sx && a[k]-sx<=500)
		/*放在头部是成立的,并且在集合当中
		这一步的主要意思就是说:我们把搜索的数放在头部来查找*/ 
		{
			ans[x]=a[k]-sx;//更新序列(占用资源)看看这个点是不是属于集合当中的 
			dfs(k+1,x+1,y,a[k],sy);//继续寻找答案,前缀和序列的数就被更新为我们要当前找到的这个成立的和 
			ans[x]=0;//如果不属于,就把位置空出来,也就是回溯(释放资源)
		}
		if(bk[a[k]-sy]==true && 1<=a[k]-sy && a[k]-sy<=500)
		/*放在尾部是成立的,并且在集合当中
		这一步的主要意思就是说:我们把搜索的数放在尾部来查找*/ 
		{
			ans[y]=a[k]-sy;//更新序列(占用资源) 
			dfs(k+1,x,y-1,sx,a[k]);//继续寻找答案,后缀和序列的数就被更新为我们要当前找到的这个成立的和 
			ans[y]=0;//把这个位置空出来(释放资源) 
		}
	}
	/*整个else归根到底,就是通过我们的求和来找到最小的序列*/
}
int main()
{
	scanf("%d",&n);//输入n 
	for(int i=1;i<=2*n;i++)scanf("%d",&a[i]);//输入 
	sort(a+1,a+2*n+1);//排序,从小到大 
	int m;scanf("%d",&m);//继续输入 
	memset(bk,false,sizeof(bk));//bk是用来判断是否成立 
	for(int i=1;i<=m;i++)
	{
		int xx;scanf("%d",&xx);//输入 
		bk[xx]=true;//既然是S集合当中,那就一定是成立的 
	}
 	dfs(1,1,n,0,0);//递归 
	return 0;
}

  

1444:埃及分数

理解!!!IDDFS,迭代加深搜索

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
LL ans[maxn],s[maxn],mo,ch;
int dep;
//迭代加深搜索
//https://www.cnblogs.com/hadilo/p/5746894.html
//https://www.cnblogs.com/hchlqlz-oj-mrj/p/5389223.html 
LL gcd(LL a,LL b){
	//返回最大公因数
	return b==0? a:gcd(b,a%b); 
}
void outp(){
	if(ans[dep]>s[dep]){  //如果结果更优 ,ans[dep]>s[dep]说明最后一位大一些 
		for(int i=1;i<=dep;i++){
			ans[i]=s[i];
		}
	}
}
void dfs(LL x,LL y,int d){
	LL a,b,i,w;
	if(d==dep){ //已经到了最后的深度了 
		//如果符合1/a的格式
		s[d]=y;
		if(x==1&&s[d]>s[d+1]) outp(); //且递减 
		return;
	}
	//注意这个下面的范围        //这个是放大 
	for(i=max(s[d-1]+1,y/x+1);i<(dep-d+1)*y/x;i++){
		b=y*i/gcd(y,i);
		a=b/y*x-b/i; //统分就知道了
		w=gcd(a,b);
		a/=w;b/=w;
		s[d]=i;
		dfs(a,b,d+1); 
	}
	
}

int main(){
	scanf("%lld%lld",&ch,&mo);	
	int i=gcd(ch,mo);
	ch/=i;
	mo/=i;
	for(dep=2;;dep++){
		ans[1]=0;
		s[0]=0;        //赋个初值
		ans[dep]=INF;   //赋个初值
		dfs(ch,mo,1);
		if(ans[1]!=0) break; 
	}
	for(int j=1;j<=dep;j++) printf("%lld ",ans[j]);
	printf("\n");
return 0;
}

  

1445:平板涂色

需要记录每个板块与之相邻的板块之间的联系

读入数据,统计颜色,然后每个颜色都试一遍,即把该颜色的且能涂的砖涂上。
下一次涂色不能涂上次涂过的色。涂完了记录结果

为了不超时,加了两个剪枝
最优化剪枝:当前涂色次数大于等于当前答案,直接退出(这个好理解吧)
可行性剪枝:如果当前一个砖都没有涂到,直接退出(如果接着搜,会多一个次数,可能还会死循环,,,)
至于判断该砖是否能涂,先预处理,把紧邻该砖上方的砖用数组记录下来,再判断那些砖是否被涂

但是这道题还是有点不太明白,标记那里。。。vis[]不同的数值代表什么

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
/*
https://www.cnblogs.com/Xchu/p/11605818.html
读入数据,统计颜色,然后每个颜色都试一遍,即把该颜色的且能涂的砖涂上。
下一次涂色不能涂上次涂过的色。涂完了记录结果

为了不超时,加了两个剪枝
最优化剪枝:当前涂色次数大于等于当前答案,直接退出(这个好理解吧)
可行性剪枝:如果当前一个砖都没有涂到,直接退出(如果接着搜,会多一个次数,可能还会死循环,,,)
至于判断该砖是否能涂,先预处理,把紧邻该砖上方的砖用数组记录下来,再判断那些砖是否被涂
*/ 
struct node{
	int a1,b1,a2,b2,x;
}a[20];
bool cmp(node a,node b){
	if(a.a1!=b.a1) return a.a1<b.a1;
	else return a.b1<b.b1;
} 
int col[20]; //表示是否有这个颜色
int n,m,ans=INF;
int vis[20],link[20][20];
//vis代表该砖是否被涂,link表示这两块砖之间是不是有联系
bool judge(int x){
	//这个是判断x上面的 是不是都涂过了 
	for(int i=1;i<=n;i++){
		if(!vis[i]&&link[x][i]) return false;  //如果i砖下面紧邻x,但i没涂过,返回false
	}
	return true;
} 
void dfs(int num,int yes,int xx){
	   // 涂色次数   涂过颜色的砖  上次涂的颜色
	   if(num>=ans) return;  //剪枝1 
	   if(yes==n) {
	   	ans=num;
	   	return;
	   } 
	   for(int i=1;i<=m;i++){ //枚举所有的颜色
	   	int times=0;
		   if(i!=xx&&col[i]){  //不是上次涂的颜色并且存在这个颜色
		   	for(int j=1;j<=n;j++){
		   		if(!vis[j]&&a[j].x==i&&judge(j)){ //剪枝2:这个砖块没有涂过,颜色也是对的,并且上面的都涂过了 
		   				vis[j]=1;
						times++; 
				   }
				//else if(vis[j]&&a[j].x==i) vis[j]++;
			   }
			   if(times>0){ //如果涂了砖,进行下一步
			   	dfs(num+1,yes+times,i);
			   }
			for(int j=n;j>=1;j--){
				if(vis[j]&&a[j].x==i&&judge(j)){ //应该加上vis[j]==1 
					vis[j]=0;
					times--;
				}
				//else if(vis[j]&&a[j].x==i) vis[j]--;
			} 
		   } 
	   	
	   }
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d %d %d %d %d",&a[i].a1,&a[i].b1,&a[i].a2,&a[i].b2,&a[i].x);
		a[i].a1++;
		a[i].b1++;
		col[a[i].x]++;
	}
	for(int i=1;i<=20;i++) if(col[i]) m=i;
	sort(a+1,a+1+n,cmp);
	for(int i=2;i<=n;i++){
		for(int j=i-1;j>=1;j--){
			if(a[i].a1==a[j].a2+1&&((a[i].b1>=a[j].b1&&a[i].b1<=a[j].b2)||(a[i].b2>=a[j].b1&&a[i].b2<=a[j].b2))){
				link[i][j]=1; //i上面直接连接j 
			}
		}
	}
	dfs(0,0,0);
	printf("%d\n",ans);
return 0;
}


#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<iomanip>
#include<cstdlib>
#include<queue>
#include<map>
#include<set>
#include<stack>
#include<vector>
#define ll long long
using namespace std;
inline int read()
{
   int s=0,w=1;
   char ch=getchar();
   while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
   while(isdigit(ch)) s=s*10+ch-'0',ch=getchar();
   return s*w;
}
struct lbq  //结构体 a1b1 该砖左上角坐标 a2b2 右下角坐标 x 颜色
{
    int a1,b1,a2,b2,x;
}a[20];
int ccmp(lbq a,lbq b)
{
    if(a.a1!=b.a1) return a.a1<b.a1;
    return a.b1<b.b1;
}
bool d=false;
int de[20];//de数组表示是否有该颜色
int n,m,ans=999,b[20],fk[20][20]; //b数组代表该砖是否被涂
bool OK(int o)
{
    for(int i=1;i<=n;i++)
        if(fk[o][i]&&!b[i]) return false; //如果i砖下面紧邻o,但i没涂过,返回false
    return true;
}
void dfs(int o,int pq,int xx)//o 涂色次数 pq 涂过颜色的砖 xx 上次涂的颜色
{
    if(o>=ans) return;//当前涂色次数大于等于当前答案,直接退出
    if(pq==n)//涂完了,记录答案
    {
        ans=o;
        return;
    }
    for(int i=1;i<=m;i++)//枚举颜色
    {
        int oj=0;//代表现在用这个颜色涂的砖数
        if(i!=xx&&de[i])//如果有这个颜色,并且这种颜色上次没用过
        {
            for(int j=1;j<=n;j++) //涂色
                if(!b[j]&&a[j].x==i&&OK(j))
				//如果没涂过该砖,并且能涂
                    b[j]=1,oj++;
                else
				if(b[j]&&a[j].x==i)
				b[j]++;
           if(oj>0)
		   dfs(o+1,pq+oj,i);//如果涂了砖,进行下一步
           for(int j=n;j>=1;j--)//回溯一步
                if(b[j]==1&&a[j].x==i&&OK(j))  ///错在这里!!!b[j]==1,这里只涂过1次!才会需要抹去 
                    b[j]=0,oj--;
                else
				if(b[j]>1&&a[j].x==i)
				b[j]--; 
        }
    }
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        a[i].a1=read(),a[i].b1=read(),a[i].a2=read(),a[i].b2=read(),a[i].x=read(),
        a[i].a1++,a[i].b1++,de[a[i].x]++;//记录颜色
    for(int i=1;i<=20;i++)
	if(de[i])
	m=i; //求最大颜色编号
    sort(a+1,a+n+1,ccmp);//按左上角坐标大小从小到大排序(先考虑纵,再考虑横)
    for(int i=2;i<=n;i++)
        for(int j=i-1;j>=1;j--)//fk[i][j]表示第i个砖是否紧邻上方第j个砖
            if(a[i].a1==a[j].a2+1&&((a[i].b1>=a[j].b1&&a[i].b1<=a[j].b2)||(a[i].b2>=a[j].b1&&a[i].b2<=a[j].b2)))
                fk[i][j]=1;//如果i砖的最上面紧邻j砖最下面,且两砖横坐标有重叠,即j砖为i砖紧邻上面的砖
    dfs(0,0,0);
    printf("%d",ans);//结果
    return 0;
}

  

 

 

poj 1426 Find The Multiple

对一个数n,求出它的倍数m,m的十进制写法只有1,0两个数字,分别进行搜索dfs(x*10,step+1),dfs(x*10+1,step+1)就可以了,开头判断能不能退出就可以了

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
//直接搜索就可以了
//你是在不知道可以先试试啊
bool flag;
int n;
LL m; 
void dfs(LL x,int step){  //前面的代表数,后面那个表示步数
	if(flag||step==19) return;
	if(x%n==0){
		m=x;
		flag=1;
		return;
	} 
	dfs(x*10,step+1);
	dfs(x*10+1,step+1);  //这两部是关键! 
	return; 
}
int main(){
	while(scanf("%d",&n)){
		if(n==0) break;
		flag=0;
		dfs(1,0);
		printf("%lld\n",m);
	}
return 0;
}

  

hud 4460

多源最短路---交朋友

朋友链的最短长度,做n次SPFA,n个点依次作为起点,求出最短距离,如果有一次的结果是INF,那么就可以退出输出了,BFS就用在了SPFA上

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
//多源最短路问题
//SPFA+暴力
//求两点最大距离
struct node{
	int to,dis,from;
	int next;
}ed[151511]; 
int head[10010];
int dis[10010];
int vis[10010];
int cnt=0;

int n,m;
void add(int from,int to,int dis){
	ed[cnt].to=to;	
	ed[cnt].dis=dis;
	ed[cnt].next=head[from];
	head[from]=cnt++;
}
int spfa(int s){
	queue<int> q;
	q.push(s);
	for(int i=1;i<=n;i++) dis[i]=0x3f3f3f3f;
	dis[s]=0;
	memset(vis,0,sizeof(vis));
	vis[s]=1;
	while(!q.empty()){
		int t=q.front();
		q.pop();
		vis[t]=0;
		for(int i=head[t];~i;i=ed[i].next){
			int v=ed[i].to;
			if(dis[v]>dis[t]+ed[i].dis){
				dis[v]=dis[t]+ed[i].dis;
				if(vis[v]==0){
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
	//直接在这判断最远
	int mi=0;
	for(int i=1;i<=n;i++){
		if(dis[i]>mi) mi=dis[i];
	} 
	if(mi==0x3f3f3f3f) return -1;
	else return mi;
}
int main(){
	while(~scanf("%d",&n)){
		char ss[1000];
		if(n==0) break;
		map<string,int> aa;
		int tot=0;
		for(int i=0;i<n;i++){
			scanf("%s",ss);
			///!!!
			if(aa[ss]==0){
				aa[ss]=++tot; //避免重复 
			} 
		}
		scanf("%d",&m);
		tot=0;
		memset(head,-1,sizeof(head));
		for(int i=0;i<m;i++){
			scanf("%s",ss);
			int u=aa[ss];
			scanf("%s",ss);
			int v=aa[ss];
			add(u,v,1);
			add(v,u,1);
		}
		//做n次spfa
		bool isok=0;
		int maxx=-INF,temp;
		for(int i=1;i<=n;i++){
			temp=spfa(i);
			if(temp==-1) {
				isok=1;
				break;
			}
			else maxx=max(maxx,temp);
		}
		if(isok) printf("-1\n");
		else printf("%d\n",maxx); 
	}
	
	
	
return 0;
}

  

 

 

 

 

 posted on 2020-02-24 20:55  shirlybabyyy  阅读(454)  评论(0)    收藏  举报