#include
#include
using namespace std;
const int N = 110;
int path[N];
int n;
bool dfs(int u, int k)//u是当前层数,k是最多允许迭代的层数
{
if (u == k) return path[u - 1] == n;//保证只有在迭代k层的时候才返回结果,保证path数组的大小就是k,只不过下标从0开始
bool st[N] = {0};//每次迭代都会重置st数组,st数组用来排除等效冗余
for (int i = u - 1; i >= 0; i -- )
for (int j = i; j >= 0; j -- )//优化搜索顺序,从大的开始搜索
{
int s = path[i] + path[j];
if (s > n || s <= path[u - 1] || st[s]) continue; //排除等效冗余,同时保证更新path[u]的数都要大于之前的
path[u] = s;
st[s] = true;
if (dfs(u + 1, k)) return true;//因为我们用的是优化的搜索顺序,从大的数开始枚举,所以只要走到这一步就说明我们之前已经找到了一个最合适的,可以去下一层了
}
return false;
}
int main ()
{
path[0] = 1;//记得初始化
while (cin >> n, n)//正常多组测试数据是给T的,题目故意恶心我们
{
int k = 1;//最多递归的层数
while (!dfs(1, k)) k ++ ;
for (int i = 0; i < k; i ++ )//dfs的时候控制每一层都得迭代k次,因为k从1开始,如果找到结果了那么一定迭代了k次,因为k++的条件就是dfs迭代没找到结果
cout << path[i] << " ";
cout << endl;
}
return 0;
}
#include
#include
#include
using namespace std;
//G[i]≤2^31−1,不能用背包,int数组存不了这么大的数字
typedef long long LL;
const int N = 46;
int w, n, k;
int weights[1 << (N / 2)], cnt;//枚举k = N/2个物品,每个物品选或者不选2种选择,因此理论上最多有2^N/2种重量组合,
int g[N];
int ans;//收集结果
void dfs1(int u, int s)//当前枚举的是第u个物品,到目前枚举的所有物品的重量和为s
{
//收集结果
if(u == k)
{
weights[cnt ++ ] = s;
return ;
}
//不选择第u个物品
dfs1(u + 1, s);
//选择第u个物品
if ((LL)s + g[u] <= w)//可行性优化
dfs1(u + 1, s + g[u]);
}
void dfs2(int u, int s)//当前枚举的是第u个物品,到目前枚举的所有物品的重量和为s
{
//收集结果
if (u == n)
{
int l = 0, r = cnt - 1;
while (l < r)//从之前打的表(前n/2个物品组合可以组成的所有重量组合)直接取数据,题目要求最大的重量,取max即可
{
int mid = l + r + 1 >> 1;
if ((LL)s + weights[mid] <= w) l = mid;
else r = mid - 1;//说明mid不满足性质,r不用取mid
}
if (weights[l] + (LL)s <= w)
ans = max(ans, weights[l] + s);//这里不能写成ans = max(ans, weights[l] + (LL)s),因为max函数的两个参数数据类型得一样
return ;
}
//不选择第u个物品
dfs2(u + 1, s);
//选择第u个物品
if ((LL)s + g[u] <= w)//可行性优化
//这里ans = max(ans, weights[l] + (LL)s)和dfs2(u + 1, s + g[u])都能过,不知道有啥区别
dfs2(u + 1, s + g[u]);
}
int main()
{
cin >> w >> n;
for (int i = 0; i < n; i ++ ) cin >> g[i];
sort(g, g + n);//优化搜索顺序
reverse(g, g + n);
k = n / 2;//将前n/2个物品可以组合成的重量打表记录
dfs1(0, 0);//枚举到第k - 1个物品,因为下标从0开始
sort(weights, weights + cnt);//优化搜索顺序
//判重
//int t = 0;//如果判重的时候t从0开始枚举,那么要特判一下weights[cnt - 1] 和 weights[cnt - 2],并且i + 1 < cnt
int t = 1;
for (int i = 1; i < cnt; i ++ )
if (weights[i] != weights[i - 1])
weights[t ++ ] = weights[i];//t 从1开始赋值更新weights数组 默认weights[0]是不重复的
cnt = t;//更新weights数组的大小
dfs2(k, 0);//从第k个物品开始枚举
cout << ans;
return 0;
}
和A*算法挺像的
结合了迭代加深 和 IDA*
#include
#include
using namespace std;
const int N = 16;
int n;
int q[N];
int w[5][N];//存储每一层,方便回溯
int f()//每一次调换两端图书的顺序会改变三个后继关系,因此这个启发函数用来发现当前图书有多少个不正确的后继关系
{
int res = 0;
for (int i = 0; i + 1< n; i ++ )
{
if (q[i + 1] != q[i] + 1) res ++ ;//一开始写成了q[i + 1] != q[i],后来写成了q[i] != q[i] + 1,debug半天
}
return (res + 2) / 3;
}
bool check()
{
for (int i = 0; i + 1 < n; i ++ )
if (q[i + 1] != q[i] + 1)
return false;
return true;
}
bool dfs(int u, int depthMax) //u为当前层数,depthMax为最多迭代的层数
{
if (u + f() > depthMax) return false;//启发函数判断是否有继续下去的必要
if (check()) return true;//不能直接用 !f()来判断,因为如果有1或2个逆序的!f()返也是0
for (int length = 1; length <= n; length ++ )
for (int l = 0; l + length - 1 < n; l ++ )
{
int r = l + length - 1;
for (int k = r + 1; k < n; k ++ )
{
memcpy(w[u], q, sizeof q);
int x, y;
for (x = r + 1, y = l; x <= k; x ++ , y ++ ) q[y] = w[u][x];
for (x = l; x <= r; x ++ , y ++ ) q[y] = w[u][x];
if (dfs(u + 1, depthMax)) return true;
memcpy(q, w[u], sizeof q);//走到这一步说明之前那个替换方法失败了,回溯
}
}
return false ;
}
int main()
{
int T;
cin >> T;
while (T -- )
{
cin >> n;
for(int i = 0; i < n; i ++ ) cin >> q[i];
int depthMax = 0;
while (depthMax < 5 && !dfs(0, depthMax)) depthMax ++ ;
if (depthMax >= 5) cout << "5 or more" << endl;
else cout << depthMax << endl;
}
return 0;
}
/*
0 1
2 3
4 5 6 7 8 9 10
11 12
13 14 15 16 17 18 19
20 21
22 23
*/
#include
#include
using namespace std;
const int N = 24;
int op[8][7] = {
{0, 2, 6, 11, 15, 20, 22},
{1, 3, 8, 12, 17, 21, 23},
{10, 9, 8, 7, 6, 5, 4},
{19, 18, 17, 16, 15, 14, 13},
{23, 21, 17, 12, 8, 3, 1},
{22, 20, 15, 11, 6, 2, 0},
{13, 14, 15, 16, 17, 18, 19},
{4, 5, 6, 7, 8, 9, 10}
};
int opposite[8] = {5, 4, 7, 6, 1, 0, 3, 2};
int center[8] = {6, 7, 8, 11, 12, 15, 16, 17};//记录中间八个数的坐标
int q[N];
int path[100];
//通过简单分析,我们可以发现每次拉扯只可以在中间多增加一个相同的数
//(拉进去一个出来一个,最理想状态为拉进去一个我们需要的数,扯出来不需要的数,所以每次最多增加一个目标数),
//所以估价函数设置为8减去中间最多的数个数就行了
int f()
{
int maxV = 0;
static int sum[4];
memset(sum, 0, sizeof sum);
for (int i = 0; i < 8; i ++ ) sum[q[center[i]]] ++ ;
for (int i = 0; i < 4; i ++ ) maxV = max(maxV, sum[i]);
return 8 - maxV;
}
void operate(int x)// x表示要进行的操作
{
int t = q[op[x][0]];
for (int i = 0;i + 1 < 7; i ++ )
{
q[op[x][i]] = q[op[x][i + 1]];
}
q[op[x][6]] = t;
}
bool dfs(int u, int depthMax, int last)
{
if (u + f() > depthMax) return false;
if (f() == 0) return true;//这题可以直接用!f()当作check函数,判断是否符合题意
for (int i = 0; i < 8; i ++ )
{
if (opposite[i] == last) continue; //之前写的i == last,debug好久,如果当前操作是上一步的反操作就continue=下一个操作i
operate(i);
path[u] = i;//不用回溯,每一层都会自己覆盖掉
if (dfs(u + 1, depthMax, i)) return true;
operate(opposite[i]);//如果走到这一步,说明操作i无法得到一个正确的结果,因此要回溯回去,遍历下一个操作是否可以得到一个正确的结果
}
return false;
}
int main ()
{
while (cin >> q[0], q[0])//新型输入输出,因为这里输入了一个,所以下面得从1开始并且循环到23
{
for (int i = 1; i <= 23; i ++ ) cin >> q[i];
int depthMax = 0;
while (!dfs(0, depthMax, -1)) depthMax ++;
if (!depthMax) cout << "No moves needed";
else
{
for (int i = 0; i < depthMax; i ++ )
printf("%c", path[i] + 'A');
}
cout << endl << q[6] << endl;
}
return 0;
}