DFS:一路到底,逐层回退。
BFS:逐层扩散。
题目一
P1588 [USACO07OPEN]Catch That Cow S
题意
一个人一个房子,给出他们的坐标 x x x, y y y。人可以
- 向前一步
- 向后一步
- 位置翻倍
求人到房子最小步数。
思路
整体就是BFS,分析一下,通过步数来看很显然他是属于逐层扩散的,一层一个步数。
然后一层的话可以扩展出三个,就是他的三种走法,这样走到的第一次的话,她就是步数最小的,当然过程中如果遇到可以通过其他较为近的途径达到的就不需要再去重新计算了,所以就是需要个标记数组,计数数组和队列数组。
代码
/**
_ ___ _______
| |/ (_) |___ / |
| ' / _ _ __ __ _ / /| |__ __ _ _ __ __ _
| < | | '_ \ / _` | / / | '_ \ / _` | '_ \ / _` |
| . \| | | | | (_| |/ /__| | | | (_| | | | | (_| |
|_|\_\_|_| |_|\__, /_____|_| |_|\__,_|_| |_|\__, |
__/ | __/ |
|___/ |___/
**/
#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
typedef pair<ll, ll> pii;
#define mem(a,x) memset(a,x,sizeof(a))
#define debug(x) cout << #x << ": " << x << endl;
#define rep(i,n) for(int i=0;i<(n);++i)
#define repi(i,a,b) for(int i=int(a);i<=(b);++i)
#define repr(i,b,a) for(int i=int(b);i>=(a);--i)
const int maxn = 2e5 + 1;
#define inf 0x3f3f3f3f
#define sf scanf
#define pf printf
#define EPS 1e-6
#define MAX 100
const int mod = 1e9 + 7;
int n,m;
int ans;
int q[maxn],vis[maxn],dis[maxn];
int main(){
int t;
cin>>t;
while(t--){
cin>>n>>m;
for(int i=0;i<=1e5+10;i++){
q[i]=0;vis[i]=0;dis[i]=0;
}
int top=0,end=1;
q[end]=n;
vis[n]=1;
int x;
while(top<=end){
top++;///表示开始处理点了
for(int i=0;i<3;i++){
x=q[top];
if(i==0) x++;
if(i==1) x--;
if(i==2) x=x*2;
if(x>=0&&x<=100000){
if(!vis[x]){///没走过
end++;
q[end]=x;
vis[x]=1;///走了
dis[x]=dis[q[top]]+1;///更新步数
}
}
}
}
cout<<dis[m]<<endl;
}
return 0;
}
题目二
P1118 [USACO06FEB]Backward Digit Sums G/S
题意
写出一个1至N的排列ai,然后每次将相邻两个数相加,构成新的序列,再对新序列进行这样的操作,显然每次构成的序列都比上一次的序列长度少1,直到只剩下一个数字位置。下面是一个例子:
3 1 2 4
4 3 6
7 9
16
最后得到16这样一个数字。
现在倒着玩这样一个游戏,如果知道N,知道最后得到的数字的大小sum,请你求出最初序列ai,为1至N的一个排列。若答案有多种可能,则输出字典序最小的那一个。n≤12, sum≤12345。
题解
首先要做这个题,必须熟悉杨辉三角。
在这里说些杨辉三角吧,虽然是初中高中的知识吧,
其实就是这样子的,就是说任选个数,他其实就是等于他两肩的数的和。
再看这个题如何跟杨辉三角扯上关系的。
a b c d e
a+b b+c c+d d+e
a+2b+c b+2c+d c+2d+e
a+3b+3c+d b+3c+3d+e
a+4b+6c+4d+e
我们可以看出最后一个(也就是第五行)的系数就是
1 4 6 4 1
再跟杨辉三角关联下就可以看出杨辉三角的第五行的系数也是
1 4 6 4 1
这是偶然现象吗?
再看下6行就知道了。
a b c d e f
a+b b+c c+d d+e e+f
a+2b+c b+2c+d c+2d+e d+2e+f
a+3b+3c+d b+3c+3d+e c+3d+3e+f
a+4b+6c+4d+e b+4c+6d+4e+f
a+5b+10c+10d+5e+f
1 5 10 10 5 1
在对比杨辉三角的第6行这不就有关系了吗
推出这个就容易多了。剩下的就是暴搜。
因为他要求字典序最小。所以我们暴搜的话就从1开始暴搜,第一次搜到的就是最优答案,每次这个数字用过就不能再用了,每个只能用一次,输出也是一次。
/**
_ ___ _______
| |/ (_) |___ / |
| ' / _ _ __ __ _ / /| |__ __ _ _ __ __ _
| < | | '_ \ / _` | / / | '_ \ / _` | '_ \ / _` |
| . \| | | | | (_| |/ /__| | | | (_| | | | | (_| |
|_|\_\_|_| |_|\__, /_____|_| |_|\__,_|_| |_|\__, |
__/ | __/ |
|___/ |___/
**/
#include <map>
#include <queue>
#include <string>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include <algorithm>
#include <math.h>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
typedef pair<ll, ll> pii;
#define mem(a,x) memset(a,x,sizeof(a))
#define debug(x) cout << #x << ": " << x << endl;
#define rep(i,n) for(int i=0;i<(n);++i)
#define repi(i,a,b) for(int i=int(a);i<=(b);++i)
#define repr(i,b,a) for(int i=int(b);i>=(a);--i)
const int maxn = 2e5 + 1;
#define inf 0x3f3f3f3f
#define sf scanf
#define pf printf
#define EPS 1e-6
#define MAX 100
const int mod = 1e9 + 7;
bool vis[20];///判断是否用过
int n,m;
int a[20];
int dp[20][20];///存储杨辉三角
bool flag;///是否输出?
void dfs(int num,int sum){
if(flag) return ;
if(num==n+1){
if(sum==m){
for(int i=1;i<n;i++){
cout<<a[i]<<" ";
}
cout<<a[n]<<endl;
flag=1;
}
return ;
}
for(int i=1;i<=n;i++){
if(vis[i]==0){
vis[i]=1;
a[num]=i;
dfs(num+1,sum+dp[n][num]*i);
vis[i]=0;
}
}
}
int main(){
cin>>n>>m;
dp[1][1]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<=i;j++)
dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
}
dfs(1,0);
return 0;
}