总结&&up
(因人而异,这只是我个人的)
- 对于 D F S DFS DFS的下次递归别乱放,否则容易 T L E TLE TLE。
- 由于
B
F
S
BFS
BFS要入队,所以要记忆化(否则可能会超内存),
防止一个位置重复入队
1.棋盘问题(类似8皇后问题: D F S DFS DFS)
题意:
有一个棋盘,只能在‘#’里放棋子,保证行列不冲突,问有几种方法。
思路:
- 用一个棋子数组,标记所放棋子的列号,保证列不冲突。
- 暴力枚举第 i i i行 的每一列是否能放棋子,(当前行,可以放也可以不放)
- 放了k个就停止。
反思:
对于 D F S DFS DFS的下次递归别乱放,否则容易 T L E TLE TLE。
AC
#include <iostream>
#include <cstring>
#define mst(x,a) memset(x,a,sizeof(x))
#define For(i,x,y) for(int i=(x); i<=(y); i++)
using namespace std;
int g[10][10],a[10];
int n,k;
string s;
int dfs(int cur, int tot)
{
int ans=0;
if(tot==k)return 1;
if(cur>n)return 0;//
For(i,1,n)
{
if(!a[i]&&g[cur][i]==1)
{
a[i]=1;
ans+=dfs(cur+1,tot+1);
a[i]=0;
//ans+=dfs(cur+1,tot);///一开始放错位置了,导致tle
}
}
ans+=dfs(cur+1,tot);//应该放在这里,因为当前行不放,那么就不用进for循环里
return ans;
}
int main()
{
while(cin>>n>>k&&(n!=-1&&k!=-1))
{
mst(g,0);
For(i,1,n)
{
cin>>s;
a[i]=0;
for(int j=0; j<s.size(); j++)if(s[j]=='#')g[i][j+1]=1;
}
cout<<dfs(1,0)<<endl;
}
return 0;
}
2.POJ 2251 Dungeon Master(三维走迷宫, B F S BFS BFS)
题意:
有一个三维迷宫,问是否能走出。如果能走出输出最短路,否则输出“IMPOSSIBLE”.
思路:
- 由于三维,所以方向多加一个
上下
即可,变成六个方向的 B F S BFS BFS - 由于要最短路,边权为1,所以直接跑 B F S BFS BFS。
AC
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <queue>
#define mp make_pair
#define mst(x,a) memset(x,a,sizeof(x))
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
int flag=0;
int g[40][40][40];
int l,r,c;
int dx[6]={0,0,0,0,-1,1};
int dy[6]={0,0,1,-1,0,0};
int dz[6]={1,-1,0,0,0,0};
int ls,xs,ys,le,xe,ye;
int ans=0;
struct point{int x,y,z,dis;};
void bfs(int z, int x, int y)
{
point fi={x,y,z,0};//定义结构体时,注意变量的赋值顺序,吐了qwq。
g[z][x][y]=1;
queue<point>q;
q.push(fi);
while(!q.empty())
{
point top=q.front();q.pop();
z=top.z,x=top.x;y=top.y;
//cout<<z<<' '<<x<<' '<<y<<endl;
//cout<<g[z][x][y]<<endl;
int dis=top.dis;
if(z==le&&xe==x&&y==ye)
{
ans=dis;
flag=1;return;
}
For(i,0,5)
{
int tx,ty,tz;
tx=x+dx[i];
ty=y+dy[i];
tz=z+dz[i];
if(tz>=1&&tz<=l&&ty<=c&&ty>=1&&tx>=1&&tx<=r&&!g[tz][tx][ty])
{
//cout<<tz<<' '<<tx<<' '<<ty<<endl;
// cout<<g[tz][tx][ty]<<endl;
g[tz][tx][ty]=1;
q.push({tx,ty,tz,dis+1});
}
}
}
}
int main()
{
while(cin>>l>>r>>c&&(l|r|c))
{
mst(g,0);flag=0;
string s[40];
For(i,1,l)
{
For(j,1,r)cin>>s[j];
//getchar();
For(j,1,r)
{
For(k,0,c-1)
{
if(s[j][k]=='S')xs=j,ys=k+1,ls=i;
if(s[j][k]=='E')xe=j,ye=k+1,le=i;
if(s[j][k]=='#')g[i][j][k+1]=1;
}
}
}
bfs(ls,xs,ys);
if(flag)cout<<"Escaped in "<<ans<<" minute(s)."<<endl;
else cout<<"Trapped!"<<endl;
}
return 0;
}
3.POJ 3278 Catch That Cow(经典 B F S BFS BFS)
题意:
给你农夫的位置和牛的位置,问你农夫最少用多少时间可以抓到牛。
农夫每一步有三种op:
- x + 1 x+1 x+1
- x − 1 x-1 x−1
- x ∗ 2 x*2 x∗2
思路:
由于是每步操作,有层次,所以跑 B F S BFS BFS。
反思:
由于
B
F
S
BFS
BFS要入队,所以要记忆化(否则可能会超内存),防止一个位置重复入队
AC
#include <iostream>
#include <queue>
#define fi first
#define se second
#define mp make_pair
using namespace std;
typedef pair<int,int>pa;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
queue<pa>q;
int vis[maxn];//注意记忆化,已经入队的元素,就不要重复入队了。否则会爆内存。
int main()
{
int n,k,ans=0;
cin>>n>>k;
vis[n]=1;
q.push(mp(n,0));
while(!q.empty())
{
int x=q.front().fi,time=q.front().se; q.pop();
if(x==k){ans=time;break;}
if(x+1<=maxn-1&&!vis[x+1])vis[x+1]=1,q.push(mp(x+1,time+1));
if(x-1>=0&&!vis[x-1])vis[x-1]=1,q.push(mp(x-1,time+1));
if(x*2<=maxn-1&&!vis[x*2])vis[x*2]=1,q.push(mp(x*2,time+1));
}
cout<<ans<<endl;
return 0;
}
4.POJ 3279 Fliptile(经典棋盘翻转问题|开灯问题)
题意:
给你一个矩阵,每个小格有一块瓦,瓦有黑白两色。
每一次翻转,都会使相邻的也翻转。
求字典序最小的翻转。
思路:
- 可以枚举第一行的switch(总共最多 c o l 2 col^2 col2)而col最大也15,所以放心枚举。
- 之后就和扫雷差不多了,有了前面的基础,去推后面的。
- 根据switch去改变第一行的颜色。第一行剩下的没变白的,由下一行改变,
- 操作结束后,最后看最后一行是否变白。
反思
- 把一个数的某一位(二进制)变为1: x ∣ = 1 < < i x|=1<<i x∣=1<<i
- 把一个数的某一位(二进制)变为0: x x x&=~(1<< i i i)
- 把一个数的某一位(二进制)取反: x x x^=1<< i i i
- 找到一个数的某一位(二进制): ( x x x>> i i i)&1
- 数组的复制。memcpy(light,orlight,sizeof(orlight))
AC
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int orlight[30];
int a,n,m;
//char s[10];
int light[30];
int result[30];//int is 4bits ,but char is 1 bit.
//if the data is 1<<8,we can just use char to store the data.
//other else ,we must use int or long long in instead.
int get_bit(int c,int i){return (c>>i)&1;}
void change(int bit, int t)
{
if(a)orlight[t]|=(1<<bit);
else orlight[t] &= ~(1<<bit);
}
void init()
{
memset(orlight,0,sizeof(orlight));
for(int i=0; i<n; i++)
{
for(int j=0; j<m; j++)
{
cin>>a;
change(j,i);
}
// cout<<(int)orlight[i]<<endl;
}
}
void turnlight(int &x, int pos)
{
x^=1<<pos;
}
/*
void copy_light()
{
for(int i=0; i<n; i++)light[i]=orlight[i];
}
*/
void revers(int t)
{
for(int i=0; i<m; i++)
{
//if(i!=m-1)
cout<<get_bit(result[t],i)<<' ';
// cout<<((result[t]>>i)&1)<<" ";ok
//cout<<((result[t]&(1<<i))>>i)<<" ";no
// else
//cout<<((result[t]>>i)&1)<<endl;
}
cout<<endl;
}
void solve()
{
int switch_num=0;
bool flag=0;
for(int i=0; i<(1<<m); i++)
{
if(flag)break;
//copy_light();
memcpy(light,orlight,sizeof(orlight));//!!!!!!!
switch_num=i;
for(int j=0; j<n; j++)
{
result[j]=switch_num;
for(int k=0; k<m; k++)
{
if((switch_num&(1<<k)))
{
turnlight(light[j],k);//light[j]^=1<<k;
if(k>0)turnlight(light[j],k-1);
if(k+1<m)turnlight(light[j], k+1);
}
}
if(j+1<n)light[j+1]^=switch_num;
switch_num=light[j];//if(j==n-1&&light[j]==0)flag=1;
}
if(light[n-1]==0)flag=1;
}
if(flag)for(int i=0; i<n; i++)revers(i);
else cout<<"IMPOSSIBLE"<<endl;
}
int main()
{
while(cin>>n>>m)
{
init();
solve();
}
return 0;
}
/*
bool check()
{
for(int i=0; i<5; i++)if(light[i]!=0)return false;
return true;
}
int getbit(int num, int bit)
{
return
}
*/
5.POJ 1426 Find The Multiple(经典 B F S BFS BFS和同余)
题意:
给你一个数n,要你找到它的一个倍数m,m只能由1,0组成。
思路:
- 对于m的最高位肯定是1.
- 之后就跑bfs。看队首元素是否满足front%n=0
- 不满足,就对m*10,之后把新的m和m+1入队.
- 由于是大数,所以用同余优化一下。
- 而答案可以通过记录操作数来取得。(操作就是*10,之后+0或者+1)
AC
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int n,cnt;
while(cin>>n&&n)
{
cnt=0;
vector<int>ans;
queue<int>q;q.push(1);
while(!q.empty())
{
int top=q.front(); q.pop();
cnt++;//统计总共进行了几次操作。
if(top%n==0)break;
q.push( (top%n*(10%n))%n );
q.push( (top%n*(10%n))%n+1 );
}
/// bfs队列常见技巧,根据总操作数,逆堆。
for(int i=cnt; i; i/=2)ans.push_back(i%2);
//由于每次操作不是加0就是1,所以可以根据总操作数,来逆推出答案
for(int i=ans.size()-1; i>=0; i--)cout<<ans[i];
cout<<endl;
}
return 0;
}