小猫爬山
问题描述
解题思路
本道题在常规的dfs上多添加了一个剪枝的操作:dfs的参数中枚举的是第几个小猫,dfs内部的循环枚举的是当前用到了第几辆车,最优性剪枝是cnt>=res,优化搜索顺序是先将a数组从大到小排序,优先搜索大的数。
AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20;
int n, m;
int cat[N], bk[N]; // cat数组表示猫的重量,bk数组表示第i辆车已经载了多重的猫
int res = N;
void dfs(int u, int cnt) //其中u表示当前枚举到第几个猫,cnt表示当前正在用第几辆车
{
//最优性剪枝
if (cnt >= res)
return;
if (u == n + 1)
{
res = cnt;
return;
}
for (int i = 1; i <= cnt; i++)
{
//可行性剪枝
if (bk[i] + cat[u] <= m)
{
bk[i] += cat[u];
dfs(u + 1, cnt);
bk[i] -= cat[u];
}
}
bk[cnt + 1] = cat[u];
dfs(u + 1, cnt + 1);
bk[cnt + 1] = 0;
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> cat[i];
//搜索顺序剪枝
sort(cat + 1, cat + n + 1, greater<int>());
dfs(1, 1);
cout << res << endl;
return 0;
}
数独
问题描述
解题思路
本题非常的麻烦和复杂,代码全程高能,具体见注释
可以参考一篇博客
AC代码
//为理清思路,记顶层为第一层
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 10010, INF = 1e9;
int n, m;
int mins[N], minv[N]; // mins表示的是从第1层到第i层最小面积的总和,minv同理代表体积
int res = INF;
int r[N], h[N]; // r和h数组分别表示当前层的半径和高度
void dfs(int u, int v, int s) // u表示当前的层数,v表示当前的体积和,s表示当前的面积和
{
if (minv[u] + v > n)
return;
if (s + mins[u] >= res)
return;
if (s + 2 * (n - v) / r[u + 1] >= res)
return; //最优性剪枝
if (u == 0) //到达最底层
{
if (v == n)
res = s; //判断此时体积是否满足要求,则可以更新答案
return;
}
//开始枚举r和h,先r后h,从大到小枚举
for (int i = min(r[u + 1] - 1, (int)sqrt(n - v - minv[u - 1] / u)); i >= u; i--)
for (int j = min(h[u + 1] - 1, n - v - minv[u - 1] / i / i); j >= u; j--)
{
h[u] = j, r[u] = i;
int t = 0;
if (u == m)
t += i * i; //如果当前是最底层,加上俯视图面积
//层数-1,面积和体积加上去
dfs(u - 1, v + i * i * j, s + 2 * i * j + t);
}
}
int main()
{
cin >> n >> m;
//预处理mins和minv,代表当前体积和与面积和的最小值
for (int i = 1; i <= m; i++)
{
mins[i] = mins[i - 1] + i * i * 2;
minv[i] = minv[i - 1] + i * i * i;
}
//设置哨兵,防止出现边界情况
r[m + 1] = h[m + 1] = INF;
//从底层往上层搜
dfs(m, 0, 0);
//用于判断无解的情况
if (res == INF)
res = 0;
cout << res << endl;
return 0;
}
木棒
问题描述
解题思路
本道题的剪枝思路也是异常的复杂,详见这篇博客
AC代码
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100;
int n;
int sum, length; // sum表示木棍长度总和,length表示木棒长度
int w[N];
bool st[N];
// u表示当前枚举到了第u根木棒,cur表示当前第u根木棒的长度,start表示当前从第几根木棍开始枚举
bool dfs(int u, int cur, int start)
{
if (u * length == sum)
return true; //如果最终符合条件,则返回true
if (cur == length)
return dfs(u + 1, 0, 1); //若当前木棒长度已满,则新开一根木棒
for (int i = start; i <= n; i++)
{
if (st[i])
continue;
if (w[i] + cur > length)
continue; //如果当前木棒长度+当前木棍长度>length,则返回
st[i] = true;
if (dfs(u, w[i] + cur, i + 1))
return true; //将这跟木棒添入木棍中,若最终合法,则返回true
//若代码执行到这里,则说明此处不应该添加第i根木棍
st[i] = false; //恢复现场
//如果这跟木棍在木棒中是第一根或者最后一根,则整个方案不合法
if (!cur || w[i] + cur == length)
return false;
//如果该木棍不能放在此处,那么和他等长的木棍也不能放在此处
while(i<=n && a[i]==a[i+1]) i++;
}
return false;
}
int main()
{
while (cin >> n, n)
{
memset(st, 0, sizeof st);
sum = 0, length = 1; //木棒长度从1开始枚举
for (int i = 1; i <= n; i++)
{
cin >> w[i];
sum += w[i];
}
//优化搜索顺序,从小到大枚举
sort(w + 1, w + n + 1, greater<int>());
while (1)
{
if (sum % length == 0 && dfs(1, 0, 1))
{
cout << length << endl;
break;
}
length++;
}
}
return 0;
}
生日蛋糕
问题描述
解题思路
是四道题目里剪枝最复杂的一题,详见这篇博客
AC代码
//为理清思路,记顶层为第一层
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 10010, INF = 1e9;
int n, m;
int mins[N], minv[N]; // mins表示的是从第1层到第i层最小面积的总和,minv同理代表体积
int res = INF;
int r[N], h[N]; // r和h数组分别表示当前层的半径和高度
void dfs(int u, int v, int s) // u表示当前的层数,v表示当前的体积和,s表示当前的面积和
{
if (minv[u] + v > n)
return;
if (s + mins[u] >= res)
return;
if (s + 2 * (n - v) / r[u + 1] >= res)
return; //最优性剪枝
if (u == 0) //到达最底层
{
if (v == n)
res = s; //判断此时体积是否满足要求,则可以更新答案
return;
}
//开始枚举r和h,先r后h,从大到小枚举
for (int i = min(r[u + 1] - 1, (int)sqrt(n - v - minv[u - 1] / u)); i >= u; i--)
for (int j = min(h[u + 1] - 1, n - v - minv[u - 1] / i / i); j >= u; j--)
{
h[u] = j, r[u] = i;
int t = 0;
if (u == m)
t += i * i; //如果当前是最底层,加上俯视图面积
//层数-1,面积和体积加上去
dfs(u - 1, v + i * i * j, s + 2 * i * j + t);
}
}
int main()
{
cin >> n >> m;
//预处理mins和minv,代表当前体积和与面积和的最小值
for (int i = 1; i <= m; i++)
{
mins[i] = mins[i - 1] + i * i * 2;
minv[i] = minv[i - 1] + i * i * i;
}
//设置哨兵,防止出现边界情况
r[m + 1] = h[m + 1] = INF;
//从底层往上层搜
dfs(m, 0, 0);
//用于判断无解的情况
if (res == INF)
res = 0;
cout << res << endl;
return 0;
}