acwing搜索

bfs 多用于求最小步数(一个状态当作一步)或者最短路,换句话说就是该解是离根最近的,所以遇到的第一个解就可以break
dfs 用于搜索所有的解,且不用记录较多的信息就可以搜索全部的解

最小步数模型

1107. 魔板

在这里插入图片描述

样例输入:

2 6 8 4 5 7 3 1

样例输出:

7
BCABCCB

代码模板:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>

using namespace std;

char g[2][4];
unordered_map<string, pair<char, string>> pre;
unordered_map<string, int> dist;

void set(string state)
{
    for (int i = 0; i < 4; i ++ ) g[0][i] = state[i];
    for (int i = 7, j = 0; j < 4; i --, j ++ ) g[1][j] = state[i];
}

string get()
{
    string res;
    for (int i = 0; i < 4; i ++ ) res += g[0][i];
    for (int i = 3; i >= 0; i -- ) res += g[1][i];
    return res;
}

string move0(string state)
{
    set(state);
    for (int i = 0; i < 4; i ++ ) swap(g[0][i], g[1][i]);
    return get();
}

string move1(string state)
{
    set(state);
    int v0 = g[0][3], v1 = g[1][3];
    for (int i = 3; i >= 0; i -- )
    {
        g[0][i] = g[0][i - 1];
        g[1][i] = g[1][i - 1];
    }
    g[0][0] = v0, g[1][0] = v1;
    return get();
}

string move2(string state)
{
    set(state);
    int v = g[0][1];
    g[0][1] = g[1][1];
    g[1][1] = g[1][2];
    g[1][2] = g[0][2];
    g[0][2] = v;
    return get();
}

int bfs(string start, string end)
{
    if (start == end) return 0;

    queue<string> q;
    q.push(start);
    dist[start] = 0;

    while (!q.empty())
    {
        auto t = q.front();
        q.pop();

        string m[3];
        m[0] = move0(t);
        m[1] = move1(t);
        m[2] = move2(t);

        for (int i = 0; i < 3; i ++ )
            if (!dist.count(m[i]))
            {
                dist[m[i]] = dist[t] + 1;
                pre[m[i]] = {'A' + i, t};
                q.push(m[i]);
                if (m[i] == end) return dist[end];
            }
    }

    return -1;
}

int main()
{
    int x;
    string start, end;
    for (int i = 0; i < 8; i ++ )
    {
        cin >> x;
        end += char(x + '0');
    }

    for (int i = 1; i <= 8; i ++ ) start += char('0' + i);

    int step = bfs(start, end);

    cout << step << endl;

    string res;
    while (end != start)
    {
        res += pre[end].first;
        end = pre[end].second;
    }

    reverse(res.begin(), res.end());

    if (step > 0) cout << res << endl;

    return 0;
}

 

双端队列广搜(边权只有0 or 1问题)

175. 电路维修

双端队列:
在这里插入图片描述

在这里插入图片描述

样例输入:

1
3 5
\\/\\
\\///
/\\\\

样例输出:

1

代码模板:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <deque>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 510, M = N * N;

int n, m;
char g[N][N];
int dist[N][N];
bool st[N][N];

int bfs()
{
    memset(dist, 0x3f, sizeof dist);
    memset(st, 0, sizeof st);
    dist[0][0] = 0;
    deque<PII> q;
    q.push_back({0, 0});

    char cs[] = "\\/\\/";   //正确的连通方式 
    int dx[4] = {-1, -1, 1, 1}, dy[4] = {-1, 1, 1, -1};
    int ix[4] = {-1, -1, 0, 0}, iy[4] = {-1, 0, 0, -1};

    while (q.size())
    {
        PII t = q.front();
        q.pop_front();

        if (st[t.x][t.y]) continue;
        st[t.x][t.y] = true;

        for (int i = 0; i < 4; i ++ )
        {
            int a = t.x + dx[i], b = t.y + dy[i];
            if (a < 0 || a > n || b < 0 || b > m) continue;
            
            int ca = t.x + ix[i], cb = t.y + iy[i];
            int d = dist[t.x][t.y] + (g[ca][cb] != cs[i]);

            if (d < dist[a][b])
            {
                dist[a][b] = d;

                if (g[ca][cb] != cs[i]) q.push_back({a, b}); //边权为1 插入队尾
                else q.push_front({a, b});  //边权为 0 插入到队头
            }
        }
    }

    return dist[n][m];
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T -- )
    {
        scanf("%d%d", &n, &m);
        for (int i = 0; i < n; i ++ ) scanf("%s", g[i]);

        int t = bfs();

        if (t == 0x3f3f3f3f) puts("NO SOLUTION");
        else printf("%d\n", t);
    }

    return 0;
}

 

双向BFS

190. 字串变换

在这里插入图片描述
样例输入:

abcd xyz
abc xu
ud y
y yz

样例输出:

3

代码模板:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <unordered_map>

using namespace std;

const int N = 6;

int n;
string A, B;
string a[N], b[N];

int extend(queue<string>& q, unordered_map<string, int>&da, unordered_map<string, int>& db, 
    string a[N], string b[N])
{
        auto t = q.front();
        q.pop();

        for (int i = 0; i < n; i ++ ) //枚举规则
            for (int j = 0; j < t.size(); j ++ ) //枚举字符串的位置
                if (t.substr(j, a[i].size()) == a[i])
                {
                    string r = t.substr(0, j) + b[i] + t.substr(j + a[i].size());//连接
                    if (db.count(r)) return da[t] + db[r] + 1;  //会师
                    if (da.count(r)) continue;  //重复continue
                    da[r] = da[t] + 1;
                    q.push(r);
                }

    return 11;
}

int bfs()
{
    if (A == B) return 0;
    queue<string> qa, qb;
    unordered_map<string, int> da, db;

    qa.push(A), qb.push(B);
    da[A] = db[B] = 0;

    int step = 0;
    while (qa.size() && qb.size())
    {
        int t; 
        //小优化 :选择队列中元素数量少的一端扩展
        if (qa.size() < qb.size()) t = extend(qa, da, db, a, b);
        else t = extend(qb, db, da, b, a);

        if (t <= 10) return t;
    }

    return 11;
}

int main()
{
    cin >> A >> B;
    while (cin >> a[n] >> b[n]) n ++ ;

    int t = bfs();
    if (t > 10) puts("NO ANSWER!");
    else cout << t << endl;

    return 0;
}

 

A*

179. 八数码

在这里插入图片描述
样例输入:

2  3  4  1  5  x  7  6  8 

样例输出:

ullddrurdllurdruldr

代码模板:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>

#define x first
#define y second

using namespace std;

typedef pair<int, string> PIS;

//启发函数: 当前位置的数到正确位置的曼哈顿距离
int f(string state)
{
    int res = 0;
    for (int i = 0; i < 9; i ++ )
        if (state[i] != 'x')
        {
            int v = state[i] - '1';
            res += abs(v / 3 - i / 3) + abs(v % 3 - i % 3);
        }

    return res;
}

string bfs(string start)
{
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    char op[5] = "urdl";

    string end = "12345678x";  //正确位置
    unordered_map<string, int> dist;
    unordered_map<string, pair<char, string>> pre; //记录路径
    //小根堆:当前实际距离 + 当前位置的估计距离 杭电oj的八数码给忘了记一下
    priority_queue<PIS, vector<PIS>, greater<PIS>> heap;

    heap.push({f(start), start});
    dist[start] = 0;

    while(heap.size())
    {
        auto t = heap.top();
        heap.pop();

        string state = t.y;
        if (state == end) break;
        
        //找x的位置
        int x, y;
        for (int i = 0; i < 9; i ++ )
            if (state[i] == 'x')
            {
                x = i / 3, y = i % 3;
                break;
            }
        
        string source = state;
        for (int i = 0; i < 4; i ++ )
        {
            int a = x + dx[i], b = y + dy[i];
            if (a < 0 || a >= 3 || b < 0 || b >= 3) continue;
            state = source;
            swap(state[x * 3 + y], state[a * 3 + b]);
            if (dist.count(state) == 0 || dist[state] > dist[source] + 1)
            {
                dist[state] = dist[source] + 1;
                pre[state] = {op[i], source};
                heap.push({dist[state] + f(state), state}); //这里是当前真实距离 + 估计距离
            }
        }
    }

    string res;
    while (end != start)
    {
        res += pre[end].x;
        end = pre[end].y;
    }
    reverse(res.begin(), res.end());//

    return res;
}

int main()
{
    string start, seq;
    char c;
    while (cin >> c)
    {
        start += c;
        if (c != 'x') seq += c;
    }

    int cnt = 0;
    for (int i = 0; i < 8; i ++ )
        for (int j = i + 1; j < 8; j ++ )
            if (seq[i] > seq[j])
                cnt ++ ;

    if (cnt % 2) puts("unsolvable");
    else cout << bfs(start) << endl;

    return 0;
}

 

178. 第K短路

在这里插入图片描述
样例输入:

2 2
1 2 5
2 1 4
1 2 2

样例输出:

14

代码模板:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;
typedef pair<int, PII> PIII;

const int N = 1010, M = 200010;

int n, m, S, T, K;
int h[N], rh[N], e[M], w[M], ne[M], idx;
int dist[N], cnt[N];
bool st[N];

void add(int h[], int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

void dijkstra()
{
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    heap.push({0, T});

    memset(dist, 0x3f, sizeof dist);
    dist[T] = 0;

    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.y;
        if (st[ver]) continue;
        st[ver] = true;

        for (int i = rh[ver]; ~i; i = ne[i])
        {
            int j = e[i];
            if (dist[j] > dist[ver] + w[i])
            {
                dist[j] = dist[ver] + w[i];
                heap.push({dist[j], j});
            }
        }
    }
}

int astar()
{
    //当前实际位置 + 估计距离 // 当前实即距离 // 元素 
    //小根堆
    priority_queue<PIII, vector<PIII>, greater<PIII>> heap;
    heap.push({dist[S], {0, S}});

    while (heap.size())
    {
        auto t = heap.top();
        heap.pop();

        int ver = t.y.y, distance = t.y.x;
        cnt[ver] ++ ;
        if (cnt[T] == K) return distance;

        for (int i = h[ver]; ~i; i = ne[i])
        {
            int j = e[i];
            if (cnt[j] < K) //把所有能扩展到的点都加进队列,来求第k最短路
                heap.push({distance + w[i] + dist[j], {distance + w[i], j}});
        }
    }

    return -1;
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h);
    memset(rh, -1, sizeof rh);

    for (int i = 0; i < m; i ++ )
    {
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        //tip: 有向图分别存储正向和反向
        add(h, a, b, c);
        add(rh, b, a, c);
    }
    scanf("%d%d%d", &S, &T, &K);
    if (S == T) K ++ ;
    //预处理估价函数: 当前位置到终点的距离
    dijkstra();
    //A*
    printf("%d\n", astar());

    return 0;
}

 

dfs之搜索顺序

1117. 单词接龙

在这里插入图片描述
样例输入:

5
at
touch
cheat
choose
tact
a

样例输出:

23

代码模板:

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 21;

int n;
string word[N];
int g[N][N];
int used[N];
int ans;

void dfs(string dragon, int last)
{
    ans = max((int)dragon.size(), ans);

    used[last] ++ ;

    for (int i = 0; i < n; i ++ )
        if (g[last][i] && used[i] < 2)
            dfs(dragon + word[i].substr(g[last][i]), i);

    used[last] -- ;
}

int main()
{
    cin >> n;
    for (int i = 0; i < n; i ++ ) cin >> word[i];
    char start;
    cin >> start;

    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ )
        {
            string a = word[i], b = word[j];
            for (int k = 1; k < min(a.size(), b.size()); k ++ )
                if (a.substr(a.size() - k, k) == b.substr(0, k))
                {
                    g[i][j] = k;
                    break;
                }
        }

    for (int i = 0; i < n; i ++ )
        if (word[i][0] == start)
            dfs(word[i], i);

    cout << ans << endl;

    return 0;
}

 

1118. 分成互质组

在这里插入图片描述
样例输入:

6
14 20 33 117 143 175

样例输出:

3

代码模板:

#include <iostream>

using namespace std;

const int N = 15;

int n;
int p[N]; // 存放数
int group[N][N]; // 存放每个组
bool st[N]; // 标记每个数是否被放进了组内
int ans; // 当前所存在的最优解

int gcd(int a, int b) { // gcd求最大公约数
    return b ? gcd(b, a % b) : a;
}

bool check(int g[], int gc, int num) { // 判断当前组中的数是否和该数都互质(即该数能否放进该组)
    for (int i = 0; i < gc; ++ i) // 枚举此组组内每个数
        if (gcd(g[i], p[num]) > 1 ) // 只要组内有数和该数不互质了就 return false
            return false;
    return true; // 否则 return true
}

//1、放进此时的最后一个组内
//2、新开一个组放该数
void dfs(int g, int gc, int tc, int start) { 
        // g为当前的最后一组的组的序号, gc为当前组内搜索到的数的序号;
        // tc为当前搜索过的数的数量, start为当前组开始搜索的数的序号

    if (g >= ans) return ; // 如果有比当前最优解所需的组数更多的解法说明此解不为最优解-->直接return即可
    if (tc == n) ans = g; // 如果搜完了所有点了,说明此解为当前的最优解,更新最优解

    bool flag = true; // flag标记是否能新开一组
    for (int i = start; i < n; ++ i) // 枚举每个数
        if (!st[i] && check(group[g], gc, i)) { // 如果当前数还未被放进组里 且 与当前的组中的数都互质
            st[i] = true; // 将该数标记为被放进组里的状态
            group[g][gc] = p[i]; // 将该数放进该组
            dfs(g, gc + 1, tc + 1, i + 1); 
                // 继续搜索该组,组内数的数量gc + 1,总的数的数量tc + 1,搜索的数的序号i + 1
            st[i] = false; // 恢复
            flag = false; // 如果能放进当前最后一组,则不用新开一组,故flag标记为false
        }

    if (flag) dfs(g + 1, 0, tc, 0); 
        // 如果无法放进最后一组,则新开一组来搜索
            // 当前最后一组的组的序号g + 1, 组内搜索的数的序号gc为0;
            // 搜索到的数tc未变, 当前组内开始搜索的数的序号start为0
    /* 此时的dfs操作其实就相当于开了一个组开始搜索的操作,还没有放数进来 */
}

int main() {
    cin >> n;
    ans = n; // 还未搜索时,假定最优解为最坏的情况每个数都分一组

    for (int i = 0; i < n; ++ i) scanf("%d", p + i);

    dfs(1, 0, 0, 0); 
    // 从第一个组g = 1, 组内没有数gc = 0;
    // 还没搜索到点tc = 0, 当前组还未开始遍历数start = 0的初始状态开始搜索

    cout << ans << endl; // 输出搜索完后的最优解

    return 0; 
}

 

dfs之剪枝与优化

166. 数独

在这里插入图片描述
样例输入:

4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......
......52..8.4......3...9...5.1...6..2..7........3.....6...1..........7.4.......3.
end

样例输出:

417369825632158947958724316825437169791586432346912758289643571573291684164875293
416837529982465371735129468571298643293746185864351297647913852359682714128574936

代码模板:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 9;
int lowbit(int x){
	return x&(-x);
}
int col[N],row[N],cell[N][N];
char str[90];
int map[1<<N],ones[1<<N];

void init(){
    
    //将行和列和方格中得状态都设为可填状态 0为不可填 1为可填
	for(int i=0;i<N;i++) col[i]=row[i]=(1<<N)-1;
	for(int i=0;i<3;i++)
		for(int j=0;j<3;j++)
			cell[i][j]=(1<<N)-1;
}

int getones(int x,int y){
    //活得行列方格中所有1(可填)的交集
	return col[y]&row[x]&cell[x/3][y/3];
}

bool dfs(int cnt){
	if(!cnt) return true;
	
	//找到可填充元素最少的位置 -> 优化搜索顺序
	int minn=10,x,y;
	for(int i=0;i<9;i++)
		for(int j=0;j<9;j++)
			if(ones[getones(i,j)]<minn&&str[i*9+j]=='.'){
				minn=ones[getones(i,j)];
				x=i,y=j;
			}
	
	
	//开始填充数字
	for(int i=getones(x,y);i;i-=lowbit(i)){ //枚举所有1(可填)
		int t=map[lowbit(i)];  //获得低位1的十进制数 map获得低位1的位置
		row[x]-=(1<<t);
		col[y]-=(1<<t);
		cell[x/3][y/3]-=(1<<t);
		str[x*9+y]='1'+t;
		if(dfs(cnt-1)) return true;
		//回溯
		row[x]+=(1<<t);
		col[y]+=(1<<t);
		cell[x/3][y/3]+=(1<<t);
		str[x*9+y]='.';
	}		
			
	return false;
	 
}
int main(){
    //预处理某状态是第几位
	for(int i=0;i<N;i++) map[1<<i]=i;
	//预处理所有状态为1(可填)的个数
	for(int i=0;i<1<<N;i++){
		int s=0;
		for(int j=i;j;j-=lowbit(j)) s++;
		ones[i]=s;
	}
	
	while(cin>>str,str[0]!='e'){
		init(); //初始化
		int cnt=N*N;
		//将题目中已经填得数字初始化行列方格
		for(int i=0;i<9;i++)
			for(int j=0;j<9;j++)
				if(str[i*9+j]!='.'){
					int t=str[i*9+j]-'1';
					row[i]-=1<<t;
					col[j]-=1<<t;
					cell[i/3][j/3]-=1<<t;
					cnt--;  //填方格-1
				}
				
		
		dfs(cnt);
		
		cout<<str<<endl;
		
    }
    return 0;
}

 

167. 木棒

在这里插入图片描述
样例输入:

9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0

样例输出:

6
5

代码模板:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

const int N = 64;

int n,sum,length;
int sticks[N];
bool st[N];

//u当前已经有多少根木棒 cur当前木棒的长度 start当前从哪根木棍开始枚举(木棍从大到小枚举)

bool dfs(int u,int cur,int start){
    //当前的木棒数量等于所有木棍的总和->当前木棒长度可行
	if(u*length==sum) return true;
	
	//当前的木棒长度可行 开始递归下一根木棒
	if(cur==length)  return dfs(u+1,0,0);
	
	//从大到小枚举木棍
	for(int i=start;i<n;i++){
		if(st[i]) continue;
		int l=sticks[i];
		if(cur+l>length) continue;
		
		st[i]=true;
		if(dfs(u,cur+l,i+1)) return true;
		st[i]=false;
		
		//如果是木棒的第一个木棍失败,则一定失败 
		if(!cur) return false;
		
		//如果是该木棒的最后一根木棒时失败时,说明前面的排法方案有问题,直接return
		if(cur+l==length) return false;
		
		// 跳过相同的木棒 
		int j=i;
		while(j<n && sticks[j]==l) j++;
		i=j-1;
	}
	
	return false;
}
int main(){
	while(cin>>n,n){
		sum=0,length=0;
		for(int i=0;i<n;i++){
			int l;
			cin>>l;
			sticks[i]=l;
			sum+=l;
			length=max(length,l);
		}
		
		//降序排序->优化搜索顺序->排除等效冗余 
		sort(sticks,sticks+n);
		reverse(sticks,sticks+n);
		
		memset(st,false,sizeof st);
		for(int i=0;i<n;i++)
			if(sticks[i]>50)
			    st[i]=true;
			    
		while(true){
		    //枚举的木棒的长度必须被sum整除
			if(sum%length==0&&dfs(0,0,0)){
				cout<<length<<endl;
				break;
			}
			length++;
		}
	}
	return 0;
}
 

 

168. 生日蛋糕

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

思路:
1.首先看到题目知道暴搜,暴力枚举r和h,由于体积公式中r的影响最大所以先枚举r再枚举h,但是r和h有范围 最小:当前层数;最大 (1)剩下的体积根据公式当高等于1时候r取得最大值 (2) 下一层的半径+1。
2.当剩下的体积小于可安排的最小体积,return;当前表面积加上可安排的最小表面积大于ans,return;
3.根据表面积和体积公式求得关系剪枝:当前已经安排表面积得加上(…大于)ans,return(最恶心且最重要得一环)
样例输入:

100
2

样例输出:

68

代码模板:

#include <bits/stdc++.h>

using namespace std;

const int M = 30;

int n, m, ans = 0x3f3f3f3f;
int minv[M], mins[M], R[M], H[M];
//自底向上递归
//v当前处理的体积和 s当前处理的面积和
void dfs(int dep, int v, int s)
{
    if (v + minv[dep] > n) return;
    if (s + mins[dep] >= ans) return; // 剪枝(4)
    if (s + 2 * (n - v) / R[dep + 1] >= ans) return; // 剪枝(5)

    if(dep == 0)
    {
        if (v == n) ans = min(ans, s);
        return;
    }

    // 剪枝(2)(3)
    for (int r = min(R[dep + 1] - 1, (int)sqrt(n - v)); r >= dep; r -- )
        for (int h = min(H[dep + 1] - 1, (n - v) / r / r); h >= dep; h -- )
        {
            R[dep] = r, H[dep] = h;
            dfs(dep - 1, v + r * r * h, s + 2 * r * h + (dep == m ? r * r : 0));
        }
}

int main()
{
    scanf("%d %d", &n, &m);

    for (int i = 1; i <= m; i ++ )
    {
        minv[i] = minv[i - 1] + i * i * i;
        mins[i] = mins[i - 1] + 2 * i * i; 
    }

    R[m + 1] = H[m + 1] = 0x3f3f3f3f;

    dfs(m, 0, 0); // 剪枝(1)

    if (ans == 0x3f3f3f3f) puts("0");
    else printf("%d\n", ans);

    return 0;
}


 

迭代加深

170.加成序列

在这里插入图片描述
样例输入:

5
7
12
15
77
0

样例输出:

1 2 4 5
1 2 4 6 7
1 2 4 8 12
1 2 4 5 10 15
1 2 4 8 9 17 34 68 77

代码模板:

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 110;

int n;
int path[N];


bool dfs(int u, int k)
{
    if (u == k) return path[u - 1] == n;

    bool st[N] = {0};  //排除等效冗余 两个数相加有重复数的可能
    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;
            st[s] = true;

            path[u] = s;
            if (dfs(u + 1, k)) return true;
        }

    return false;
}


int main()
{
    path[0] = 1;
    while (cin >> n, n)
    {
        int k = 1;
        while (!dfs(1, k)) k ++ ;
        for (int i = 0; i < k; i ++ ) cout << path[i] << ' ';
        cout << endl;
    }

    return 0;
}

 

双向DFS

171.送礼物(dfs排序+二分)

在这里插入图片描述

样例输入:

20 5
7
5
4
18
1

样例输出:

19

代码模板:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

typedef long long LL;
const int N = 1<<24;
int w,n,g[N];
int weight[N],k,cnt;
int ans;
void dfs(int u,int s){
	if(u==k){
		weight[cnt++]=s;
		return;
	}
	
	if((LL)s+g[u]<=w) dfs(u+1,s+g[u]);
	
	dfs(u+1,s);
}

void dfs2(int u,int s){
	if(u==n){
		int l=0,r=cnt-1;
		while(l<r){
			int mid=l+r+1>>1;
			if(weight[mid]+(LL)s>w) r=mid-1;
			else l=mid; 
		}
		if((LL)s+weight[r]<=w) ans=max(ans,weight[r]+s);
		
		return;
	}
	
	if((LL)s+g[u]<=w) dfs2(u+1,s+g[u]);
	
	dfs2(u+1,s);
}

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+2;
	
	dfs(0,0);
	
	sort(weight,weight+cnt);
	int t=1;
	for(int i=1;i<cnt;i++)
	    if(weight[i]!=weight[i-1])
	    	weight[t++]=weight[i];
	    	
	cnt=t;
	
	dfs2(k,0);
	
	cout<<ans<<endl;
	
	return 0;
} 

 

IDA*

IDA* + 迭代加深
当前深度 + 估价函数 > 深度限制 —> return;

180. 排书

在这里插入图片描述
在这里插入图片描述

样例输入:

3
6
1 3 4 6 2 5
5
5 4 3 2 1
10
6 8 5 3 4 7 2 9 1 10

样例输出:

2
3
5 or more

代码模板:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 15;

int n;
int q[N], w[5][N];

int f() //估价函数
{
    int res = 0;
    for (int i = 0; i + 1 < n; i ++ )
        if (q[i + 1] != q[i] + 1)
            res ++ ;
    return (res + 2) / 3;
}

bool check()  
{
    for (int i = 0; i < n; i ++ )
        if (q[i] != i + 1)
            return false;
    return true;
}
 //depth当前深度   max_depth最大深度
bool dfs(int depth, int max_depth)
{
    if (depth + f() > max_depth) return false; //当前深度加上估价函数  
    if (check()) return true;  //是否升序

    for (int l = 0; l < n; l ++ )
        for (int r = l; r < n; r ++ )
            for (int k = r + 1; k < n; k ++ )
            {
                //备份
                memcpy(w[depth], q, sizeof q);
                int x, y;
                //位置交换
                for (x = r + 1, y = l; x <= k; x ++, y ++ ) q[y] = w[depth][x];
                for (x = l; x <= r; x ++, y ++ ) q[y] = w[depth][x];
                if (dfs(depth + 1, max_depth)) return true;   
                memcpy(q, w[depth], sizeof q);      //恢复现场
            }
    return false;
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T -- )
    {
        scanf("%d", &n);
        for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);

        int depth = 0;
        //迭代加深
        while (depth < 5 && !dfs(0, depth)) depth ++ ;
        if (depth >= 5) puts("5 or more");
        else printf("%d\n", depth);
    }

    return 0;
}

 

181. 回转游戏

在这里插入图片描述
样例输入:

1 1 1 1 3 2 3 2 3 1 3 2 2 3 1 2 2 2 3 1 2 1 3 3
1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3
0

样例输出:

AC
2
DDHH
2

代码模板:

/*
      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 <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 24;

int q[N];
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 center[8] = {6, 7, 8, 11, 12, 15, 16, 17};
int opposite[8] = {5, 4, 7, 6, 1, 0, 3, 2};

int path[100];

int f()
{
    static int sum[4];
    memset(sum, 0, sizeof sum);
    for (int i = 0; i < 8; i ++ ) sum[q[center[i]]] ++ ;

    int s = 0;
    for (int i = 1; i <= 3; i ++ ) s = max(s, sum[i]);
    return 8 - s;
}

bool check()
{
    for (int i = 1; i < 8; i ++ )
        if (q[center[i]] != q[center[0]])
            return false;
    return true;
}

void operation(int x)
{
    int t = q[op[x][0]];
    for (int i = 0; i < 6; i ++ ) q[op[x][i]] = q[op[x][i + 1]];
    q[op[x][6]] = t;
}

bool dfs(int depth, int max_depth, int last)
{
    if (depth + f() > max_depth) return false;
    if (check()) return true;

    for (int i = 0; i < 8; i ++ )
    {
        if (opposite[i] == last) continue;
        operation(i);
        path[depth] = i;
        if (dfs(depth + 1, max_depth, i)) return true;
        operation(opposite[i]);
    }

    return false;
}

int main()
{
    while (scanf("%d", &q[0]), q[0])
    {
        for (int i = 1; i < N; i ++ ) scanf("%d", &q[i]);
        int depth = 0;
        
        while (!dfs(0, depth, -1)) depth ++ ;
        if (!depth) printf("No moves needed");
        for (int i = 0; i < depth; i ++ ) printf("%c", 'A' + path[i]);
        printf("\n%d\n", q[6]);
    }

    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值