Day1
搜索
- 深度优先搜索
- 事实上,深度优先搜索属于图算法的一种,英文缩写为DFS即
Depth First Search
。其过程简要来说是对每一个可能的分支路径深入到不能再深入为止,而且每个节点只能访问一次.
举例说明之:下图是一个无向图,如果我们从A点发起深度优先搜索(以下的访问次序并不是唯一的,第二个点既可以是B也可以是C,D),则我们可能得到如下的一个访问过程:A->B->E(没有路了!回溯到A)->C->F->H->G->D(没有路,最终回溯到A,A也没有未访问的相邻节点,本次搜索结束).
简要说明深度优先搜索的特点:每次深度优先搜索的结果必然是图的一个连通分量.深度优先搜索可以从多点发起.如果将每个节点在深度优先搜索过程中的"结束时间"排序(具体做法是创建一个list,然后在每个节点的相邻节点都已被访问的情况下,将该节点加入list结尾,然后逆转整个链表),则我们可以得到所谓的"拓扑排序",即topological sort
。
- 事实上,深度优先搜索属于图算法的一种,英文缩写为DFS即
- 广度优先搜索
八皇后
题目描述
一个如下的\(6*6\)的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
![八皇后][1]
上面的布局可以用序列\(2 4 6 1 3 5\)来描述,第\(i\)个数字表示在第&i&行的相应位置有一个棋子,如下:
行号 \(1 2 3 4 5 6\)
列号 \(2 4 6 1 3 5\)
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 3 个解。最后一行是解的总个数。
输入格式
一行一个正整数 n,表示棋盘是 n×n 大小的。
输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
输入
6
输出
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4
说明/提示
【数据范围】
对于\(100%\)的数据,6 \le n \le 136≤n≤13。
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int a[25];
int n, ans=0;
bool vis[25];
void dfs(int x)
{
if(x==n+1)
{
ans++;
if(ans<=3)
{
for(int i=1; i<=n; i++) printf("%d ", a[i]);
putchar('\n');
}
return ;
}
for(int i=1; i<=n; i++)
{
if(vis[i]==1) continue;
bool ok=1;
for(int j=1; j<x; j++)
{
if(abs(x-j)==abs(i-a[j]))
{
ok=0;
break;
}
}
if(ok)
{
a[x]=i;
vis[i]=1;
dfs(x+1);
vis[i]=0;
}
}
return ;
}
int main()
{
scanf("%d", &n);
dfs(1);
printf("%d ", ans);
return 0;
}
八数码
题目描述
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
输入格式
输入初始状态,一行九个数字,空格用0表示
输出格式
只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)
输入
283104765
输出
4
#include <cstdio>
#include<map>
#include<queue>
#include<algorithm>
#define ll long long
using namespace std;
const ll dx[]={-1,0,0,1}, dy[]={0,-1,1,0};
ll n;
bool check(int nx, int ny)
{
return(nx<0||ny<0||nx>2||ny>2);
}
int main()
{
scanf("%lld", &n);
queue<ll> q;
q.push(n);
map<ll, ll> m;
m[n] = 0;
while(!q.empty())
{
int u = q.front();
int c[3][2], f=0, g=0, n=u; q.pop();
if(u == 123804765) break;
for(ll i=2; i>=0; i--)
{
for(ll j=2; j>=0; j--)
{
c[i][j] = n%10, n/=10;
if(!c[i][j]) f=i, g=j;
}
}//状态转移
for(ll i=0; i<4; i++)//移动
{
ll nx=f+dx[i], ny=g+dy[i], ns=0;
if(check(nx, ny)) continue;
swap(c[nx][ny], c[f][g]);
for(ll i=0; i<3; i++)
{
for(ll j=0; j<3; j++) ns = ns*10+c[i][j];
{
if(!m.count(ns))
{
m[ns] = m[u]+1;
q.push(ns);
}
swap(c[nx][ny], c[f][g]);
}
}
}
}
printf("%d", m[123804765]);
return 0;
}
生日蛋糕
题目背景
7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层
生日蛋糕,每层都是一个圆柱体。
设从下往上数第i(1<=i<=M)层蛋糕是半径为\(R_i\),高度为\(H_i\)的圆柱。当i<M时,要求\(R_i>R_{i+1}\)且\(H_i>H_{i+1}\)
由于要在蛋糕上抹奶油,为尽可能节约经费,我们希望蛋糕外表面(最下一层的下底面除外)的面积Q最小。
令\(Q= Sπ\)
请编程对给出的N和M,找出蛋糕的制作方案(适当的Ri和Hi的值),使S最小。
(除Q外,以上所有数据皆为正整数)
题目描述
![生日蛋糕][3]
输入格式
有两行,第一行为N(N<=20000),表示待制作的蛋糕的体积为Nπ;第二行为M(M<=15),表示蛋糕的层数为M。
输出格式
仅一行,是一个正整数S(若无解则S=0)。
输入
100
2
输出
68
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int a[21], b[21], m, n, ans;
void search(int v, int s, int p, int r, int h)
{
int hh;
if (p == 0)
{
if (v==n && s<ans)//判断是否符合要求并得到更优解
ans = s;//更新
return ;
}
if(v+b[p-1] > n) return ;//体积超出
if(s+a[p-1] > ans) return ;//表面积超出
if(2*(n-v)/r+s >= ans) return; //当前的表面积+余下的侧面积>当前最优值
//剩余表面积FS>=2*V剩/r
//若FS+s>=ans 则不符合
for(int i=r-1; i>=p; i--)//枚举上一层的半径
{
if(p == m) s = i*i;
hh = min((n-v-b[p-1])/(i*i),h-1);
for(int j=hh; j>=p; j--)//枚举上一层的高
search(v+i*i*j, s+2*i*j, p-1, i, j);
}
}
int main()
{
scanf("%d%d", &n, &m);
ans = 2147483647;
a[0] = b[0] = 0;
for(int i = 1; i<21; i++)
{
a[i] = a[i-1]+2*i*i;//第i层使用的最大表面积
b[i] = b[i-1]+i*i*i;//第i层使用的最大体积
}
search(0, 0, m, n+1, n+1);
if(ans == 2147483647) printf("0");
else printf("%d", ans);
return 0;
}
- 上面大部分是搜索,当然还有剪枝