问题一:
P1036 选数
问题分析:
正确代码:
#include
using namespace std;
int nums[25];
int sm;
int n,k;
bool Issu(int x)
{
for(int i=2;i<=sqrt(x);i++)
{
if(x%i==0)
{
return false;
}
}
return true;
}
void dfs(int m,int sum,int starts)
{//深度优先遍历 先加第一个元素再加第二个元素,此时主控权在第二个元素中,再往深入到第三个元素,不符合就退回上一层元素
if(m==k)
{
if(Issu(sum))
{
sm++;//记录素数的个数
}
else{
return;//不符合就退回上一级
}
}
/*增加元素*/
for(int i=starts;i<=n;i++)//循环提供要增加的元素
{
dfs(m+1,sum+nums[i],i+1);//从开始位置进入新一个
}//不符合就去找当前退回元素的下一个 的元素
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>nums[i];
}
dfs(0,0,1);
cout<<sm;
}
代码分析:
问题二:
P1706 全排列问题
问题分析:
正确代码:
#include
using namespace std;
int n,pd[100],used[100];//pd是判断是否用过这个数
void print()//输出函数
{
int i;
for(i=1;i<=n;i++)
printf("%5d",used[i]);//保留五位常宽
cout<<endl;
}
void dfs(int k)//深搜函数,当前是第k格
{
int i;
if(k==n) //填满了的时候
{
print();//输出当前解
return;
}
for(i=1;i<=n;i++)//1-n循环填数
{
if(!pd[i])//如果当前数没有用过
{
pd[i]=1;//标记一下
used[k+1]=i;//把这个数填入数组
dfs(k+1);//填下一个
pd[i]=0;//回溯
}
}
}
int main()
{
cin>>n;
dfs(0);//注意,这里是从第0格开始的!
return 0;
}
这道题还能运用STL的next_permutation函数,比深搜更容易
问题三:
P1219 [USACO1.5]八皇后 Checker Challenge
问题分析:
正确代码:
#include
#include
using namespace std;
int n;
int col[15];
int lefts[50];//左对角
int rights[50];
int cnt;//方法
/*八皇后问题:一次搜索一行,每行放一个皇后,不能同列,因此列作标记*/
/*每画一个棋子,都要标记它的所有左上到右下以及左下到右上*/
/*棋盘上的每个坐标,左对角线上的元素行列差值相同*/
/*右对角线的行列和相同*/
int s[50];//记录路径
void Printf()
{
for(int i=1;i<=n;i++)
{
cout<<s[i]<<" ";
}
cout<<endl;
}
void dfs(int x)//下次搜索下一行
{
if(x>n)
{
cnt++;
if(cnt<=3)
{
Printf();
}
// Printf();
// memset(col,0,sizeof(col));//清零 不能打印一次就清零,因为是逐步退回
return;//搜索结束
}else{
for(int i=1;i<=n;i++)//不一定要到每一个点,可以搜索一行再利用for循环搜索每一个坐标
{//右对角线不必担心
if(col[i]==0&&lefts[x-i+n]==0&&rights[x+i]==0)//还要判断是否同斜行
{/*左对角线的行列差值范围在[1-n,n-1]可能得到负数,所以要改变它的区间*/
col[i] = 1;
lefts[x-i+n]=1;
rights[x+i]=1;
/*完全没必要再开一个变量记录每一行所在列的下标,因为行是有序的*/
s[x] = i;//第x行 的列
dfs(x+1);
col[i] = 0;
lefts[x-i+n]=0;
rights[x+i]=0;
}
}
}
}
int main()
{
//int n;
cin>>n;
dfs(1);
cout<<cnt;
}
代码分析:
问题四:
P1101 单词方阵
走这
问题分析:
最后想不出就去看了正确代码,深感自己的弱鸡
正确代码:
#include
using namespace std;
char mp[120][120];
bool tmp[120][120];//不一定要再创造一个char数组,符合就给它赋值yizhong,可以在输出时加一些条件
int n;
int row[12000];//100*100的字母矩阵,y不止100个
int col[12000];//标记y的横纵坐标
int r;
string s = "yizhong";
int xx[8] = {-1,1,0,0,-1,1,-1,1};//存储方向的数组
int yy[8] = {0,0,-1,1,-1,1,1,-1};
void dfs(int x,int y)
{
for(int i=0;i<8;i++)
{
//通过循环更改方向
int flag = 1;
for(int j=1;j<s.size();j++)
{//比较字符串是否相等 这里相当于初中的斜率知识
if(mp[x+j*xx[i]][y+j*yy[i]]!=s[j]||(x+j*xx[i])<1||(x+j*xx[i])>n||(y+j*yy[i])<1||(y+j*yy[i])>n)//要将字符串与搜索方向相同的字符比较
{/*不等比等于更好判断*/
flag = 0;
break;
}
}
if(flag)
{
for(int j=0;j<s.size();j++)
{
tmp[x+j*xx[i]][y+j*yy[i]]=true;//如果相等,就将增长方向全部改为true
}
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cin>>mp[i][j];
if(mp[i][j]=='y')
{
r++;
row[r]=i;
col[r]=j;
}
}
}
for(int i=0;i<=n+1;i++)
{
mp[i][0] = '*';
mp[i][n+1] = '*';
mp[0][i] = '*';
mp[n+1][i] = '*';
}
for(int m= 1;m<=r;m++)
{
dfs(row[m],col[m]);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(!tmp[i][j])
{
cout<<"*";
}
else{
cout<<mp[i][j];
}
}
cout<<endl;
}
}
代码分析:
问题五:
互为质因数
走这
信息奥赛一本通的题目
问题分析:
AC代码:
#include
using namespace std;
/*没必要一个一个地求公因数,直接让组里元素的积与待加入元素求是否互质*/
long long ji[15];
int n;
long long nums[15];
int mins = 0xfffffff;
void dfs(long long x,int k)//x是第几个元素 k是组数
{
if(x==n+1) {
mins = min(k,mins);
}else{
for(int i=1;i<=k;i++)//用下标就省取了一个循环
{
if(__gcd(ji[i],nums[x])==1){
ji[i]*=nums[x];
dfs(x+1,k);
ji[i]/=nums[x];
}
}
/*如果找不到组*/
ji[k+1] *= nums[x];
dfs(x+1,k+1);
ji[k+1] /=nums[x];
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>nums[i];
ji[i] = 1;
}
dfs(1,0);
cout<<mins<<endl;
}
问题六:
P3956 棋盘
走这
问题分析:
记一下自己的WA代码:
#include
using namespace std;
int mp[110][110];//地图
bool vis[110][110];//拜访坐标
int xx[4]={0,1,0,-1};
int yy[4]={1,0,-1,0};
int m,n;
int mins = 0xfffffff;
/*剪枝策略:当金币数已经大于Mins,就没必要再搜索下去*/
//bool flag;
//WA9 :不知道为什么
void dfs(int x,int y,int coin,bool magic,bool flag)//此点是否用过魔法,即将退回点是否用了魔法
{
if(x==m&&y==m)
{
mins = min(coin,mins);
return;
}
else{
for(int i=0;i<4;i++)
{
int nx = x+xx[i];
int ny = y+yy[i];
if(nx>0&&nx<m+1&&ny>0&&ny<m+1&&!vis[nx][ny])//首先不能超过地图边界
{
if(mp[nx][ny]==mp[x][y]&&mp[nx][ny]!=-1)//如果同色 ,条件有瑕疵,存在同无色的情况
{
vis[nx][ny]=true;
if(magic)
{
magic = false;
flag = true;
}
dfs(nx,ny,coin,magic,flag);
if(!magic&&flag)
{
magic = true;
flag = false;
}
vis[nx][ny] = false;
}
else if(mp[nx][ny]!=mp[x][y]&&mp[nx][ny]!=-1)
{
coin++;
if(coin>mins)
{
return;
}
vis[nx][ny]=true;
if(magic)
{
magic = false;
flag = true;
}
dfs(nx,ny,coin,magic,flag);
if(!magic&&flag)
{
magic = true;
flag = false;
}
vis[nx][ny] = false;
coin--;
}
else if(mp[nx][ny]==-1&&!magic)//需要变色
{
magic = true;
coin+=2;
if(coin>mins)
{
return;
}
vis[nx][ny]=true;
mp[nx][ny] = mp[x][y];
dfs(nx,ny,coin,magic,flag);
vis[nx][ny] = false;
mp[nx][ny]= -1;
magic = false;
coin-=2;
}
}
}
}
}
int main()
{
//freopen("C:\\Users\\86182\\Desktop\\question\\P3956_3.in","r",stdin);
ios::sync_with_stdio(0);
cin>>m>>n;
//fill(mp[1],mp[1]+m*m,-1);//填充 还没搞懂怎么填充mp,先放着
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++)
{
mp[i][j] = -1;
}
}
for(int i=1;i<=n;i++)
{
int x,y,z;
cin>>x>>y>>z;
mp[x][y] = z;
}
vis[1][1] = true;
dfs(1,1,0,false,false);
if(mins!=0xfffffff)
{
cout<<mins<<endl;
return 0;
}
else{
cout<<-1<<endl;
}
}
真是又臭又长…
参考大佬写的AC代码:
#include
using namespace std;
int mp[110][110];//地图
bool vis[110][110];//拜访坐标
int pri[110][110];//记录最优解
int xx[4]={0,1,0,-1};
int yy[4]={1,0,-1,0};
int m,n;
int mins = 0xfffffff;
/*
剪枝策略:当金币数已经大于Mins,就没必要再搜索下去(依旧TLE,优化程度不够)
2.如果是每轮搜索都要改变的量,最好放在dfs的参数中
3.用一个二维数组存储每个点的最优解,如果金币数大于最优解,就没必要搜索 (比1更优化)
4.最优解数组起始必须全为大值
*/
void dfs(int x,int y,bool magic,int coin)
{
if(coin<pri[x][y])
{
pri[x][y] = coin;
}
else
{
return;
}
if(x==m&&y==m)
{
mins = min(coin,mins);
return;
}
if(coin>mins)
{
return;
}
else{
for(int i=0;i<4;i++)
{
int nx = x+xx[i];
int ny = y+yy[i];
if(nx>0&&nx<m+1&&ny>0&&ny<m+1&&!vis[nx][ny])//首先不能超过地图边界
{
if(mp[nx][ny]!=-1)//有色
{
if(mp[nx][ny]==mp[x][y])
{
vis[nx][ny]=true;
dfs(nx,ny,false,coin);//如果直接写false,就不用再写一个flag变量
//如果是从无色格子来,到有色格子变成false
//回溯后又变成true(无色格)或者false(有色格)(即变回原来的亚子)
//这样就不用设计一个标记变量判断到新格子是否用过魔法
vis[nx][ny] = false;
}
else{
vis[nx][ny]=true;
dfs(nx,ny,false,coin+1);
vis[nx][ny] = false;
}
}
else{//如果无色
if(!magic)
{
vis[nx][ny]=true;
/*染色*/
mp[nx][ny] = mp[x][y];
dfs(nx,ny,true,coin+2);
vis[nx][ny] = false;
mp[nx][ny] = -1;
}
}
}
}
}
}
int main()
{
// freopen("C:\\Users\\86182\\Desktop\\question\\P3956_3.in","r",stdin);
ios::sync_with_stdio(0);
cin>>m>>n;
//fill(mp[1],mp[1]+m*m,-1);//填充 还没搞懂怎么填充mp,先放着
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++)
{
mp[i][j] = -1;
pri[i][j] = 0xfffffff;
}
}
for(int i=1;i<=n;i++)
{
int x,y,z;
cin>>x>>y>>z;
mp[x][y] = z;
}
vis[1][1] = true;
dfs(1,1,false,0);
if(mins!=0xfffffff)
{
cout<<mins<<endl;
return 0;
}
else{
cout<<-1<<endl;
}
}