板子
ACW841
//1-n按字典序的所有排列
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 11;
int n,q[N];
bool st[N];
void dfs(int u)
{
if(u==n)
{
for(int i=0;i<u;++i)
{
cout<<q[i]<<' ';
}
cout<<endl;
return;
}
for(int i=1;i<=n;++i)
{
if(st[i])
continue;
q[u]=i;
st[i]=true;
dfs(u+1);
st[i]=false;
//q[u]=0; 可有可无 有也会被覆盖
}
}
int main()
{
cin>>n;
dfs(0);
return 0;
}
ACW842
//n皇后
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 11;
char mp[N][N];
bool col[N],g[2*N],ug[2*N];//2*N见下
int n;
void dfs(int u)
{
if(u==n+1)
{
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
cout<<mp[i][j];
cout<<endl;
}
cout<<endl;
return;
}
for(int i=1;i<=n;++i)
{
if(col[i]==true || g[u+i]==true || ug[i-u+n]==true)//①画图 ②对应为啥2*n
continue;
col[i]=true,g[u+i]=true,ug[i-u+n]=true;
mp[u][i]='Q';
dfs(u+1);
col[i]=false,g[u+i]=false,ug[i-u+n]=false;
mp[u][i]='.';
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
mp[i][j]='.';
dfs(1);
return 0;
}
正文参考lg题单 未来会删会补
1.lgP1219 裸题
#include<bits/stdc++.h>
using namespace std;
const int N=11;
int n,res,ans;
int q[15],mp[N][N];
bool col[N],g[2*N],ug[2*N];
void dfs(int u)
{
if(u==n+1)
{
if(ans++>=3)
return;
res+=1;
for(int i=1;i<u;i++)
{
cout<<q[i]<<' ';
}
cout<<endl;
}
for(int i=1;i<=n;i++)
{
if(col[i] || g[u+i] || ug[i-u+n])
continue;
col[i]=g[u+i]=ug[i-u+n]=true;
mp[u][i]=1;
q[u]=i;
dfs(u+1);
col[i]=g[u+i]=ug[i-u+n]=false;
mp[u][i]=0;
//q[u]=0;
}
}
int main()
{
cin>>n;
dfs(1);
cout<<res;
return 0;
}
2.lgP1036
n=20-----层数太深不剪枝必t 如何剪枝?
开始理解是开一个数组(int就够 和最大应该是5e7很友好),然后每筛一个和不是素数的就标记下再遇到就跳过,结果还是会t,想了想为啥,一开始只是觉得是不是和相同的太少了,即使算一下加和次数最多的应该是C(20)10,也不会有太多相同的,看了题解瞬间明白:为什么不能从dfs里的for剪???这玩意儿是指数增长的啊!将dfs抽象为一个n叉树不难理解,到很深层搜索次数的底数还都固定,但是指数却越来越高,那么减少底数显然是最明智的选择!底数体现在for循环的次数上,引出了-----“不降原则”如下。
比如1 2 3 4中选三个数不能重复如何选?
一般都是以:
123
124
134
234
这种顺序选择的,即当前元素只选比自己大且符合题意的组合。
基于这种思想不难理解dfs中另标记当前已搜索到的元素是哪一个,递归搜只搜当前元素后面的并将其加和到总和并判断是否是素数即可。代码如下:
#include<bits/stdc++.h>
using namespace std;
const int N=30;
const int M=2e8+10;
int n,k,a[N],res;
bool st[N];
typedef long long ll;
ll sum;
bool isprime(int x)//试除
{
for(int i=2;i<=x/i;++i)
{
if(x%i==0)
return false;
}
return true;
}
void dfs(int u,int y)//y标记当前已搜到的元素
{
if(u==k && isprime(sum))
{
res++;
return;
}
y++;//从当前后开始选
for(int i=y;i<=n;i++)
{
sum+=a[i];
u++;//规定几个元素加和,不能越界
dfs(u,i);//再搜就搜其后面的,即枚举该加和到sum的i
sum-=a[i];
u--;
}
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>a[i];
dfs(0,0);
cout<<res;
return 0;
}
3.lgP2036(这Perket搜了下就是老外的炖肉,汤很稠)
开始想贪心,想了老半天这怎么贪啊还是实现不了,看了题解和标签发现思路都不对做个锤子…确实是没注意到n最大是10,这么小直接搜。
#include<bits/stdc++.h>
using namespace std;
int res=0x3f3f3f3f,cum=1,sum=0,n;//说明了未爆int
struct node
{
int x,y;
}t[1000010];
bool cmp(node a,node b)//自定义酸苦都由大到小排
{
if(a.x>b.x)
return true;
else if(a.y>b.y)
return true;
}
void dfs(int u)
{
if(u==n+1)
{
if(cum==1 && sum==0)//说明一组数据都没加入 且什么都不放时就是清水
return;
res=min(res,abs(cum-sum));
return;
}
sum+=t[u].y,cum*=t[u].x;//这个数据可能用也可能不用 所以搜两次
dfs(u+1);
sum-=t[u].y,cum/=t[u].x;
dfs(u+1);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>t[i].x>>t[i].y;
sort(t+1,t+1+n,cmp);
dfs(1);
cout<<res;
return 0;
}
4 lgP1433
标签状压可还没看到dp一章…在题单里就说明能搜,一看数据n<=15不小,没办法只能剪咯。
//没剪的版本
#include <bits/stdc++.h>
using namespace std;
double sum=0,res=0x3f3f3f3f;
int n;
int a[25],b[25];
bool st[25];
double dis(int x,int y)
{
return sqrt(pow(a[x]-a[y],2)+pow(b[x]-b[y],2));
}
void dfs(int u,int now)
{
return;
if(u==n)
{
res=min(res,sum);
return;
}
for(int i=1;i<=n;i++)
{
if(st[i]==true)
continue;
sum+=dis(now,i);
st[i]=true;
dfs(u+1,i);
sum-=dis(now,i);
st[i]=false;
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i];
dfs(0,0);
printf("%.2lf",res);
return 0;
}

正常,下了个数据是n=12,想了半天不知道怎么剪,确实太菜,看了老哥题解明白了,如下:
#include <bits/stdc++.h>
using namespace std;
double sum=0,res=0x3f3f3f3f;
int n;
double a[25],b[25];
bool st[25];
double dis(int x,int y)
{
return sqrt(pow(a[x]-a[y],2)+pow(b[x]-b[y],2));
}
void dfs(int u,int now)
{
if(sum>res)//这里倒是能明白既然爆最大值了按正常思维来想肯定不是最小距离,直接舍了就好,可能也是因为数据也确实比较水...不过应该是我太菜了,确实觉得这样有些突兀,有点意想不到......
return;
if(u==n)
{
res=min(res,sum);
return;
}
for(int i=1;i<=n;i++)
{
if(st[i]==true)
continue;
sum+=dis(now,i);
st[i]=true;
dfs(u+1,i);
sum-=dis(now,i);
st[i]=false;
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i];
dfs(0,0);
printf("%.2lf",res);
return 0;
}

又看了看能理解的题解,搜索确实逃不过状压优化,我想整个模块先看一看…所以先留一坑,不出意外dp题单应该也有这道题,日后补。
状压优化如下:
在这里插入代码片
5.lgP1605
#include <bits/stdc++.h>
using namespace std;
const int N=10;
int n,m,k,res;
int sx,sy,fx,fy;
int mp[N][N];
bool st[N][N];
int dx[4]={0,0,-1,1},dy[4]={1,-1,0,0};
void dfs(int x,int y)
{
if(x==fx && y==fy)
{
res++;
return;
}
for(int i=0;i<4;i++)
{
int ix=x+dx[i];
int iy=y+dy[i];
if(ix<1 || iy<1 || ix>n || iy>m || mp[ix][iy]==1 || st[ix][iy]==true)
continue;
st[ix][iy]=true;
dfs(ix,iy);
st[ix][iy]=false;
}
return;
}
int main()
{
cin>>n>>m>>k;
cin>>sx>>sy>>fx>>fy;
while(k--)
{
int xx,yy;
cin>>xx>>yy;
mp[xx][yy]=1;
}
st[sx][sy]=true;//千万不能丢!!!
dfs(sx,sy);
cout<<res;
return 0;
}
6 lgP1019
#include <bits/stdc++.h>
using namespace std;
const int N=110;
char mp[N][N];
bool st[N][N];
int dx[8]={0,0,-1,-1,-1,1,1,1,};
int dy[8]={-1,1,-1,0,1,-1,0,1};
int n;
string s="yizhong";
void dfs(int x,int y)
{
for(int i=0;i<8;i++)
{
int flag=1;
for(int j=1;j<7;j++)//判断当前方向上的七个字符是否为“yizhong”
{
int ix=x+j*dx[i];
int iy=y+j*dy[i];
if(mp[ix][iy]!=s[j] || ix<1 || ix>n || iy<1 || iy>n)
{
flag=0;//该方向上不是“逐 字 符 相 同”(hhhhh)则标记不合格并跳出
break;
}
}
if(flag==1)
{
for(int j=0;j<7;j++)
{
int ix=x+j*dx[i];
int iy=y+j*dy[i];
st[ix][iy]=true;
}
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cin>>mp[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(mp[i][j]=='y')
dfs(i,j);//从有y字符的地方开始搜
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(st[i][j]==true)
cout<<mp[i][j];
else
cout<<'*';
}
cout<<endl;
}
return 0;
}
7 lgP1019
对我这种新手有难度,字符串操作有点复杂,搜索本身还可以。
#include <bits/stdc++.h>
using namespace std;
const int N=25;
string a[N];
int n,res;
int st[N];//<2
char q;
void dfs(string x,int l)//x是当前待被添加字符串的字符串
{
res=max(res,l);
for(int i=1;i<=n;i++)
{
int p=1;//相同子串的长度
int la=x.length(),lb=a[i].length();
while(p<min(la,lb))//所有字符串不能被完全覆盖,所以相同子串的长度p最多为min(la,lb)-1
{
if(x.substr(la-p)==a[i].substr(0,p) && st[i]<2)
{
st[i]++;
dfs(a[i],l+lb-p);//相等则继续以改变后的字符串后缀继续搜,且总合并的字符串长度变长
st[i]--;
break;//找到就跳出,得出当前p可使条件满足
}
p++;
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
cin>>q;
for(int i=1;i<=n;i++)
{
if(a[i][0]==q)
{
st[i]++;
dfs(a[i],a[i].length());
st[i]--;
}
}
cout<<res;
return 0;
}
8.lgP2404
#include <bits/stdc++.h>
using namespace std;
const int N=25;
int n,sum,q[N];
void print(int x)
{
for(int i=1;i<x;i++)
cout<<q[i]<<'+';
cout<<q[x]<<endl;
}
void dfs(int u)
{
for(int i=q[u-1];i<=sum;i++)//不减原则--新赋值的一定>=上一个赋值的元素
{
if(i==n)
break;
sum-=i;
q[u]=i;//第一个元素从u==1开始
if(sum==0)
print(u);
else
dfs(u+1);
sum+=i;
//q[u]=0;
}
}
int main()
{
cin>>n;
sum=n;
q[0]=1;
dfs(1);
return 0;
}
9.lgP1596
#include <bits/stdc++.h>
using namespace std;
const int N=110;
int n,m,res;
char mp[N][N];
bool st[N][N];
int dx[8]={0,0,-1,-1,-1,1,1,1};
int dy[8]={-1,1,1,-1,0,1,-1,0};
void dfs(int x,int y)
{
st[x][y]=true;
for(int i=0;i<8;i++)
{
int ix=x+dx[i],iy=y+dy[i];
if(ix<1 || iy<1 || ix>n || iy>m || mp[ix][iy]=='.' || st[ix][iy]==true)
continue;
st[ix][iy]=true;
dfs(ix,iy);
}
return;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>mp[i][j];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mp[i][j]=='W' && st[i][j]==false)
{
dfs(i,j);
res++;
}
}
}
cout<<res;
return 0;
}
看了下题解觉得这样搜更好:
#include <bits/stdc++.h>
using namespace std;
const int N=110;
int n,m,res;
char mp[N][N];
bool st[N][N];
int dx[8]={0,0,-1,-1,-1,1,1,1};
int dy[8]={-1,1,1,-1,0,1,-1,0};
void dfs(int x,int y)
{
mp[x][y]='.';
for(int i=0;i<8;i++)
{
int ix=x+dx[i],iy=y+dy[i];
if(ix<1 || iy<1 || ix>n || iy>m || mp[ix][iy]=='.')
continue;
mp[ix][iy]='.';//走过的坑都用'.'覆盖,避免重复
dfs(ix,iy);
}
return;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>mp[i][j];
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(mp[i][j]=='W')
{
dfs(i,j);
res++;
}
}
}
cout<<res;
return 0;
}
10 lgP2392
个人觉得这个题对我一新手来讲很不错,有一点思维的难度。
只能一科一科完成,科目间互不影响,问题转化为单科。
单科中能同时进行两个作业,所以将所有作业分成两组,搜索两组作业用时分别同时最接近sum/2即为单科用时最短。
怎么搜索?其实只需要搜两组作业中的一组,另外一组自然就出来了。令一组nowtime在<=sum/2的前提下,选出其中最大的一个且标记为maxtime,另一组用时则是sum-maxtime,因为一科花费的总时间要向大的看齐,即这一科总时间为sum-maxtime。
#include <bits/stdc++.h>
using namespace std;
int s[5],a[25];//s存每科作业数目 a存每个作业耗时
int nowtime,maxtime,sum,maxdeep;
//nowtime:当前用时总和少的一组的的作业时间总和
//maxtime:搜索该将哪些作业添加到用时总和少的一组后得到的<=sum/2且用时更大的作业时间总和
//sum:单科总花费时间
//maxdeep:单科最大作业数
void dfs(int x)
{
if(x==maxdeep+1)//maxdeep是每一科的最大作业数,超过则跳出
{
maxtime=max(maxtime,nowtime);
return;
}
if(nowtime+a[x]<=sum/2)//当前元素合法 加且递归
{
nowtime+=a[x];
dfs(x+1);
nowtime-=a[x];
}
dfs(x+1);//不合法则不加 直接递归下一个元素
}
int main()
{
int ans=0;
cin>>s[1]>>s[2]>>s[3]>>s[4];
for(int i=1;i<=4;i++)
{
maxdeep=s[i];//每科都要初始化
nowtime=0;
maxtime=0;
sum=0;
for(int j=1;j<=s[i];j++)
{
cin>>a[j];
sum+=a[j];
}
dfs(1);
ans+=sum-maxtime;//每组总花费时间向大的看齐
}
cout<<ans;
return 0;
}
3468





