搜索杂题

深搜

P1378 油滴扩展

我们枚举这个扩展的顺序即可 每次在扩展之前计算出半径即可

半径的计算方法:首先将半径设置为和四条边界的距离最小值 再枚举所有已经确定半径的点 来更新最小半径即可

#include<bits/stdc++.h>
using namespace std;
#define inl inline
#define eb emplace_back
#define endl '\n'
#define pii pair<int,int>
#define mkp make_pair
#define fi first 
#define se second 
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
#define int long long
const int N = 1e5 + 5;
const double P = 3.14159265358;

int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , vis[N];
double maxx , sx , sy , tx , ty ;

struct node { int x , y; double r; } a[N];

double calc ( int i )
{
	double res = min ( min ( abs ( a[i].x - sx ) , abs ( a[i].x - tx ) ) , min ( abs ( a[i].y - sy ) , abs ( a[i].y - ty ) ) );
	for ( int j = 1 ; j <= n ; j ++ ) 
		if ( vis[j] && i != j ) 
		{
			double d = sqrt ( ( a[i].x - a[j].x ) * ( a[i].x - a[j].x ) + ( a[i].y - a[j].y ) * ( a[i].y - a[j].y ) );
			res = min ( res , max ( d - a[j].r , 0.0 ) );
		}
	return res;
}

void dfs ( int stp , double sum )
{
	if ( stp > n ) return maxx = max ( maxx , sum ) , void();
	for ( int i = 1 ; i <= n ; i ++ )
		if ( !vis[i] ) 
		{
			a[i].r = calc(i);
			vis[i] = 1;
			dfs ( stp + 1 , sum + a[i].r * a[i].r * P );
			vis[i] = 0;
		}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read();
	sx = read() , sy = read() , tx = read() , ty = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i].x = read() , a[i].y = read();
	dfs ( 1 , 0 );
	cout << ( int ) ( abs ( sx - tx ) * abs ( sy - ty ) - maxx + 0.5 ) << endl;
	return 0;
}

P1120 小木棍

剪枝题

正常搜索思路显然 即为枚举目标长度 并枚举小木棍尝试去拼成这个长度

剪枝1:我们可以将枚举小木棍转为枚举值域 这样就可以做到从大到小拼木棍

剪枝2:前面的木棍一定比后面的木棍长 所以我们枚举值域的时候可以从上一个使用的木棍开始枚举

剪枝3(重点):如果这个拼凑方案不合法或者加上这个木棍已经拼好了 那么没有必要继续向下枚举(前提是这个木棍本身是合法的 即不会超出 \(limlen\) 的限制

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define print(x) cout<<#x<<' '<<x<<endl
const int N = 100 + 5;
const int inf = 0x3f3f3f3f;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , a[N] , cnt , maxx , tot , minn = inf , have[N];

void dfs ( int stp , int limstp , int len , int limlen , int lst )
{
	if ( stp > limstp ) cout << limlen << endl , exit(0);
	if ( len == limlen ) { dfs ( stp + 1 , limstp , 0 , limlen , maxx ); return; }
	for ( int i = lst ; i >= minn ; i -- )
		if ( have[i] && len + i <= limlen )
		{
			have[i] -- , dfs ( stp , limstp , len + i , limlen , i ) , have[i] ++;
			if ( len == 0 || len + i == limlen ) return;
		}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read();
	for ( int i = 1 ; i <= n ; i ++ )
	{
		int x = read();
		if ( x <= 50 ) a[++cnt] = x , ++ have[a[cnt]] , maxx = max ( maxx , x ) , minn = min ( minn , x ) , tot += x;
	}
	sort ( a + 1 , a + cnt + 1 );
	for ( int i = maxx ; i <= tot ; i ++ ) 
		if ( tot % i == 0 ) dfs ( 1 , tot / i , 0 , i , maxx );
	return 0;
}

P1380 T型骨牌

可以先将四种状态需要覆盖的四周节点先预处理在数组中

然后从 \((2,2)\) 搜到 \((n-1,m-1)\) 即可

但这样会 \(T\) \(3\) 个点

所以加一个剪枝:设置 \(d[x][y]\) 表示走到 \((x,y)\) 节点的最优方案

显然只有 \(sum+1\ge d[x][y]\) 的时候才能更新

\(ps:\) 预处理数组不能出错()

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define print(x) cout<<#x<<' '<<x<<endl
#define int long long
const int N = 15 + 5;
const int inf = 0x3f3f3f3f3f3f3f3f;
int dx[4][5] = { { -1 , 1 , 0 , 0 , 0 } , { -1 , 0 , 1 , 1 , 1 } , { -1 , 0 , 0 , 0 , 1 } , { -1 , -1 , -1 , 0 , 1 } };
int dy[4][5] = { { -1 , -1 , -1 , 0 , 1 } , { 0 , 0 , 0 , -1 , 1 } , { 1 , 1 , 0 , -1 , 1 } , { -1 , 0 , 1 , 0 , 0 } };

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , vis[N][N] , maxx , d[N][N];

void dfs ( int x , int y , int sum )
{
    if ( x == n ) return maxx = max ( maxx , sum ) , void();
    for ( int i = 0 ; i < 4 ; i ++ )
    {
    	int flag = 0;
		for ( int j = 0 , tx , ty ; j < 5 ; j ++ )
		{
			tx = x + dx[i][j] , ty = y + dy[i][j];
			if ( vis[tx][ty] ) { flag = 1; break; }
		}
		if ( flag ) continue;
		for ( int j = 0 , tx , ty ; j < 5 ; j ++ ) tx = x + dx[i][j] , ty = y + dy[i][j] , vis[tx][ty] = 1;
		if ( sum + 1 >= d[x][y] )
		{
			d[x][y] = max ( d[x][y] , sum );
			if ( y == m - 1 ) dfs ( x + 1 , 2 , sum + 1 );
			else dfs ( x , y + 1 , sum + 1 );
		}
		for ( int j = 0 , tx , ty ; j < 5 ; j ++ ) tx = x + dx[i][j] , ty = y + dy[i][j] , vis[tx][ty] = 0;
	}
	if ( y == m - 1 ) dfs ( x + 1 , 2 , sum );
	else dfs ( x , y + 1 , sum );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
    n = read() , m = read();
    if ( n < 3 || m < 3 ) return cout << 0 << endl , 0;
    dfs ( 2 , 2 , 0 );
    cout << maxx << endl;
	return 0;

P1092 [NOIP2004 提高组] 虫食算

最朴素的想法是枚举每一个字母对应什么数字 但是显然不优

考虑优化 我们从竖式的后面向前枚举字母 并枚举它对应的数字 然后我们考虑剪枝:

从后往前枚举竖式 如果遇到一整个竖行都确定了数字的情况 我们判断 \(x+y\)\(\bmod n\) 意义下是否等于 \(z\) 即可 还需要算进位()

注意最后一位的进位要设置为 \(0\)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define print(x) cout<<#x<<' '<<x<<endl
const int N = 2e6 + 5;
const int inf = 0x3f3f3f3f;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , a[N] , flag , ans[N] , vis[N] , cnt , q[N];

string s[4];

int check()
{
	int jin = 0;
	for ( int i = n - 1 ; i >= 0 ; i -- )
	{
		int x = ans[s[1][i]-64] , y = ans[s[2][i]-64] , z = ans[s[3][i]-64];
		if ( x != -1 && y != -1 && z != -1 )
		{
			if ( jin != -1 )
			{
				if ( ( x + y + jin ) % n != z ) return 0;
				if ( i == 0 && ( x + y + jin ) > n ) return 0;
				jin = ( x + y + jin ) / n;
			}
			else
			{
				if ( ( x + y + 1 ) % n != z && ( x + y ) % n != z ) return 0;
				if ( i == 0 && ( x + y ) > n ) return 0;
			}
		}
		else jin = -1;
	}
	return 1;
}

void dfs ( int stp )
{
	if ( stp > n ) { for ( int i = 1 ; i <= n ; i ++ ) cout << ans[i] << ' '; exit(0); }
	for ( int i = 0 ; i < n ; i ++ )
		if ( !vis[i] )
		{
			vis[i] = 1 , ans[q[stp]-64] = i;
			if ( check() ) dfs ( stp + 1 );
			vis[i] = 0 , ans[q[stp]-64] = -1;
		}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read();
	cin >> s[1] >> s[2] >> s[3];
	for ( int i = n - 1 ; i >= 0 ; i -- )
		for ( int j = 1 ; j <= 3 ; j ++ )
			if ( !vis[s[j][i]] )
			{
				vis[s[j][i]] = 1;
				q[++cnt] = s[j][i];
			}
	memset ( vis , 0 , sizeof vis );
	memset ( ans , -1 , sizeof ans );
	dfs ( 1 );
	return 0;
}

P1074 [NOIP2009 提高组] 靶形数独

我们可以先从行中确定的数多的行开始填写即可 状态可以状压可以数组

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define print(x) cout<<#x<<' '<<x<<endl
const int N = 1e2 + 5;
const int inf = 0x3f3f3f3f;

int mp[10][10] = 
{
	{ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
	{ 0 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 },
	{ 0 , 6 , 7 , 7 , 7 , 7 , 7 , 7 , 7 , 6 },
	{ 0 , 6 , 7 , 8 , 8 , 8 , 8 , 8 , 7 , 6 },
	{ 0 , 6 , 7 , 8 , 9 , 9 , 9 , 8 , 7 , 6 },
	{ 0 , 6 , 7 , 8 , 9 , 10 , 9 , 8 , 7 , 6 },
	{ 0 , 6 , 7 , 8 , 9 , 9 , 9 , 8 , 7 , 6 },
	{ 0 , 6 , 7 , 8 , 8 , 8 , 8 , 8 , 7 , 6 },
	{ 0 , 6 , 7 , 7 , 7 , 7 , 7 , 7 , 7 , 6 },
	{ 0 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 , 6 },
};

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int a[N][N] , maxx = -1 , l[N][N] , r[N][N] , k[N][N];

struct node { int x , cnt; } line[N];

int calc()
{
	int res = 0;
	for ( int i = 1 ; i <= 9 ; i ++ ) 
		for ( int j = 1 ; j <= 9 ; j ++ )
			res += mp[i][j] * a[i][j];
	return res;
}

int which ( int x , int y ) { return ( x - 1 ) / 3 * 3 + ( y - 1 ) / 3 + 1; }

int check ( int x , int y , int val )
{
	if ( l[x][val] == 1 ) return 0;
	if ( r[y][val] == 1 ) return 0;
	if ( k[which(x,y)][val] == 1 ) return 0;
	return 1;
}

void mark ( int x , int y , int val )
{
	if ( val > 0 ) l[x][val] = r[y][val] = k[which(x,y)][val] = 1 , a[x][y] = val;
	else l[x][a[x][y]] = r[y][a[x][y]] = k[which(x,y)][a[x][y]] = 0 , a[x][y] = 0;
}

void dfs ( int stp , int x , int y )
{
	if ( stp == 10 ) maxx = max ( maxx , calc() ) , void();
	if ( y == 10 ) return dfs ( stp + 1 , line[stp+1].x , 1 ) , void();
	if ( !a[x][y] )
	{
		for ( int i = 1 ; i <= 9 ; i ++ )
		{
			if ( !check ( x , y , i ) ) continue;
			mark ( x , y , i );
			dfs ( stp , x , y + 1 );
			mark ( x , y , 0 );
		} 
	}
	else dfs ( stp , x , y + 1 );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	for ( int i = 1 ; i <= 9 ; i ++ )
	{
		line[i].x = i;
		for ( int j = 1 ; j <= 9 ; j ++ )
		{
			a[i][j] = read();
			if ( a[i][j] ) ++ line[i].cnt , mark ( i , j , a[i][j] );
		}
	}
	sort ( line + 1 , line + 9 + 1 , [](const node &a , const node &b) { return a.cnt > b.cnt; } );
	dfs ( 1 , line[1].x , 1 );
	cout << maxx << endl;
	return 0;
}

P3475 [POI2008] POD-Subdivision of Kingdom

状压深搜 可以用 \(\_\_builtin\_popcount\) 函数来统计一个二进制数中 \(1\) 的个数

#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int inf = 0x3f3f3f3f;
const int N = 27;

int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
} 

int n , m , e[N] , s , ans = inf;

int count ( int x ) { return __builtin_popcount(x); }

void dfs ( int stp , int cnt , int sum , int s1 , int s2 )
{
	if ( cnt == n / 2 )
	{
		if ( sum < ans ) ans = sum , s = s1;
		return;
	}
	for ( int i = stp + 1 ; i <= n ; i ++ ) dfs ( i , cnt + 1 , sum - count ( e[i] & s1 ) + count ( e[i] & s2 ) , s1 | 1 << i - 1 , s2 ^ 1 << i - 1 );
} 

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 , u , v ; i <= m ; i ++ ) u = read() , v = read() , e[u] |= 1 << v - 1 , e[v] |= 1 << u - 1;
	dfs ( 0 , 0 , 0 , 0 , ( 1 << n ) - 1 );
	for ( int i = 1 ; i <= n ; i ++ ) if ( s & 1 << i - 1 ) cout << i << ' ';
    return 0;
}

P1283 平板涂色

状压解法在状压博客中。

显然搜索解法更加易于理解,还是先预处理上面的矩形。

之后搜索覆盖 \(n\) 个矩形,用 \(vis\) 数组来记录访问过的所有矩形,每一次记录上一次的颜色,如果和上一次的颜色不一样,那么答案 \(+1\),否则不变,继续转移

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define eb emplace_back
#define pii pair<int,int>
#define mid (l+r>>1)
#define getchar() cin.get()
const int N = 20 + 5;
const int inf = 0x3f3f3f3f;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int ans = inf , n , vis[N];

struct node { int lx , ly , rx , ry , col; } a[N];

vector<int> up[N];

int check ( int x )
{
	for ( auto v : up[x] ) if ( !vis[v] ) return 0; 
	return 1;
}

void dfs ( int stp , int sum , int lst )
{
	if ( stp == n + 1 ) return ans = min ( ans , sum ) , void();
	for ( int i = 1 ; i <= n ; i ++ )
		if ( !vis[i] && check(i) )
		{
			vis[i] = 1;
			dfs ( stp + 1 , sum + ( lst != a[i].col ) , a[i].col );
			vis[i] = 0;
		}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i].lx = read() , a[i].ly = read() , a[i].rx = read() , a[i].ry = read() , a[i].col = read();
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = 1 ; j <= n ; j ++ ) //judge j is i up
			if ( a[j].rx == a[i].lx && i != j && a[j].ly <= a[i].ry && a[j].ry >= a[i].ly ) 
				up[i].eb(j);
	dfs ( 1 , 0 , 0 );
	cout << ans << endl;
	return 0;
}

广搜

P1535 [USACO08MAR] Cow Travelling S

我们枚举所有路径即可 达到目标点之后统计即可

#include <bits/stdc++.h>
using namespace std;
#define int long long 
const int N = 100 + 5;
int n , m , a[N][N] , cnt , T , sx , sy , tx , ty;
char ch;
int dx[5] = { 0 , 1 , -1 , 0 , 0 };
int dy[5] = { 0 , 0 , 0 , 1 , -1 };


void dfs ( int x , int y , int stp )
{
	if ( !stp ) { if ( x == tx && y == ty ) cnt ++; return; }
	if ( stp < abs ( x - tx ) + abs ( y - ty ) ) return;
	for ( int i = 1 ; i <= 4 ; i ++ )
	{
		int xx = x + dx[i] , yy = y + dy[i];
		if ( xx < 1 || xx > n || yy < 1 || yy > m || a[xx][yy] ) continue;
		dfs ( xx , yy , stp - 1 );
	}	
	
}

signed main ()
{
	scanf ( "%lld%lld%lld" , &n , &m , &T );
	for ( int i = 1 ; i <= n ; i ++ )
		for ( int j = 1 ; j <= m ; j ++ )
		{
			cin >> ch;
			if ( ch == '.' ) a[i][j] = 0;
			else a[i][j] = 1;
		}
	scanf ( "%lld%lld%lld%lld" , &sx , &sy , &tx , &ty ); 
	dfs ( sx , sy , T );
	printf ( "%lld" , cnt );
	return 0;
}

P5195 [USACO05DEC] Knights of Ni S

双向广搜 从起始点广搜 并从骑士所在节点广搜 那么我们枚举所有灌木丛后取 \(min\) 即可(因为我们只需要取一个灌木丛即可)

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define print(x) cout<<#x<<' '<<x<<endl
const int N = 1e3 + 5;
const int inf = 0x3f3f3f3f;
int dx[4] = { 0 , 0 , 1 , -1 };
int dy[4] = { 1 , -1 , 0 , 0 };


int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , a[N][N] , ans = inf , dis1[N][N] , dis2[N][N] , vis[N][N] , sx , sy , tx , ty , cnt;

struct node { int x , y; } tree[N*N] , q[N*N];

void bfs1 ( int sx , int sy )
{
	memset ( vis , 0 , sizeof vis );
	memset ( dis1 , inf , sizeof dis1 );
	int head = 1 , tail = 0;
	dis1[sx][sy] = 0 , q[++tail] = { sx , sy };
	while ( head <= tail )
	{
		node u = q[head++];
		int x = u.x , y = u.y;
		for ( int i = 0 ; i < 4 ; i ++ )
		{
			int tx = x + dx[i] , ty = y + dy[i];
			if ( 1 <= tx && tx <= n && 1 <= ty && ty <= m && !vis[tx][ty] && a[tx][ty] != 1 && a[tx][ty] != 3 )
			{
				if ( dis1[tx][ty] <= dis1[x][y] + 1 ) continue;
				dis1[tx][ty] = dis1[x][y] + 1; 
				vis[tx][ty] = 1 , q[++tail] = { tx , ty };
			}
		}
	}
}

void bfs2 ( int sx , int sy )
{
	memset ( vis , 0 , sizeof vis );
	memset ( dis2 , inf , sizeof dis2 );
	int head = 1 , tail = 0;
	dis2[sx][sy] = 0 , q[++tail] = { sx , sy };
	while ( head <= tail )
	{
		node u = q[head++];
		int x = u.x , y = u.y;
		for ( int i = 0 ; i < 4 ; i ++ )
		{
			int tx = x + dx[i] , ty = y + dy[i];
			if ( 1 <= tx && tx <= n && 1 <= ty && ty <= m && !vis[tx][ty] && a[tx][ty] != 1 )
			{
				if ( dis2[tx][ty] <= dis2[x][y] + 1 ) continue;
				dis2[tx][ty] = dis2[x][y] + 1; 
				vis[tx][ty] = 1 , q[++tail] = { tx , ty };
			}
		}
	}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	m = read() , n = read();
	for ( int i = 1 ; i <= n ; i ++ ) 
		for ( int j = 1 ; j <= m ; j ++ ) 
		{
			a[i][j] = read();
			if ( a[i][j] == 2 ) sx = i , sy = j;
			if ( a[i][j] == 3 ) tx = i , ty = j;
			if ( a[i][j] == 4 ) tree[++cnt] = (node){ i , j };
		}
	bfs1 ( sx , sy ) , bfs2 ( tx , ty );
	for ( int i = 1 ; i <= cnt ; i ++ ) ans = min ( ans , dis1[tree[i].x][tree[i].y] + dis2[tree[i].x][tree[i].y] );
	cout << ans << endl;
	return 0;
}

P4011 孤岛营救问题

状压广搜 用二进制数记录钥匙获取情况即可

\(csp-s\ 2023\ rp++\)

#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int N = 10 + 5;
const int dx[4] = { 1 , -1 , 0 , 0 };
const int dy[4] = { 0 , 0 , 1 , -1 };

int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
} 

int n , m , p , s , k , wall[N][N][N][N] , door[N][N][N][N];
vector<int> key[N][N];

struct node { int x , y , dis , state; friend bool operator < ( const node &a , const node &b ) { return a.state == b.state ? ( a.x == b.x ? a.y < b.y : a.x < b.x ) : a.state < b.state; } };
queue<node> q;
map<node,int> vis;

int check ( int x , int y ) { return 1 <= x && x <= n && 1 <= y && y <= m; }

void bfs ()
{
	int st = 0;
	for ( auto v : key[1][1] ) st |= 1 << v - 1;
	q.push ( (node) { 1 , 1 , 0 , st } );
	vis[(node){1,1,0,st}] = 1;
	while ( !q.empty() )
	{
		node u = q.front(); q.pop();
		int x = u.x , y = u.y , dis = u.dis , state = u.state;
		if ( x == n && y == m ) { cout << dis << endl; exit(0); }
		for ( int i = 0 ; i < 4 ; i ++ ) 
		{
			int tx = x + dx[i] , ty = y + dy[i];
			if ( !check ( tx , ty ) || wall[tx][ty][x][y] || ( door[tx][ty][x][y] && !( ( 1 << door[tx][ty][x][y] - 1 ) & state ) ) ) continue;
			int tempst = state;
			for ( auto v : key[tx][ty] ) tempst |= 1 << v - 1;
			if ( vis.count((node){tx,ty,0,tempst}) ) continue;
			vis[(node){tx,ty,0,tempst}] = 1;
			q.push ( (node) { tx , ty , dis + 1 , tempst } );
		}
	}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read() , p = read() , k = read();
	for ( int i = 1 ; i <= k ; i ++ ) 
	{
		int x = read() , y = read() , tx = read() , ty = read() , op = read();
		if ( op > 0 ) door[x][y][tx][ty] = door[tx][ty][x][y] = op;
		else wall[x][y][tx][ty] = wall[tx][ty][x][y] = 1;
	}
	s = read();
	for ( int i = 1 ; i <= s ; i ++ )
	{
		int x = read() , y = read() , w = read();
		key[x][y].eb(w);
	}
	bfs();
	cout << -1 << endl;
    return 0;
}

Phillip and Trains

看霏神帖子的题

显然可以广搜(实际上深搜更优 因为这题求的是可行性而不是最短路)

需要记录 \(vis\) 数组表示 \(i,j\) 是否被访问过 否则会 \(MLE\)

// LUOGU_RID: 133057628
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define getchar() cin.get()
#define print(x) cout<<#x<<'='<<x<<endl
const int N = 1e3 + 5;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , k , stx , sty , mp[N][N] , vis[N][N];
char ch;

int bfs ( int sx , int sy )
{
    queue<pii> q;
    q.push({sx,sy});
    vis[sx][sy] = 1;
    while ( !q.empty() )
    {
        int x = q.front().fi , y = q.front().se; q.pop();
        // cout << x << ' ' << y << endl;
        if ( y >= n ) return 1;
        if ( mp[x][y+1] ) continue;
        for ( int i = -1 ; i <= 1 ; i ++ )
            if ( 1 <= x + i && x + i <= 3 )
                if ( !mp[x+i][y+1] && !mp[x+i][y+2] && !mp[x+i][y+3] && !vis[x+i][y+3] ) vis[x+i][y+3] = 1 , q.push({x+i,y+3});
    }
    return 0;
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
    int T = read();
    while ( T -- )
    {
        memset ( vis , 0 , sizeof vis );
        memset ( mp , 0 , sizeof mp );
        n = read() , k = read();
        for ( int i = 1 ; i <= 3 ; i ++ )
            for ( int j = 1 ; j <= n ; j ++ )
            {
                cin >> ch , mp[i][j] = ( ch != '.' && ch != 's' );
                if ( ch == 's' ) stx = i , sty = j;
            }
        cout << ( bfs ( stx , sty ) ? "YES" : "NO" ) << endl;
    }
    return 0;
}

折半搜索

P4799 [CEOI2015 Day2] 世界冰球锦标赛

折半搜索 加深了 \(lower\_bound\)\(upper\_bound\) 的用法

序列中查找第一个大于等于: \(lower\_bound\)

第一个大于: \(upper\_bound\)

最后一个小于等于: \(upper\_bound-1\)

最后一个小于: \(lower\_bound-1\)

#include<bits/stdc++.h>
using namespace std;
#define inl inline
#define eb emplace_back
#define endl '\n'
#define pii pair<int,int>
#define mkp make_pair
#define fi first 
#define se second 
// #define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
#define int long long
const int N = 40 + 5;

int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

vector<int> vec[2];
int n , m , a[N] , ans;

void dfs ( int rt , int sum , int st , int ed )
{
	if ( sum > m ) return;
	if ( st > ed ) return vec[rt].eb(sum) , void();
	dfs ( rt , sum + a[st] , st + 1 , ed );
	dfs ( rt , sum , st + 1 , ed );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	int mid = n >> 1;
	dfs ( 0 , 0 , 1 , mid );
	dfs ( 1 , 0 , mid + 1 , n );
	sort ( vec[1].begin() , vec[1].end() );
	for ( auto v : vec[0] ) ans += upper_bound ( vec[1].begin() , vec[1].end() , m - v ) - vec[1].begin();
	cout << ans << endl;
	return 0;
}

P5194 [USACO05DEC] Scales S

类似 先枚举所有的价值组合 再在前一半中找到第最后一个小于等于 \(m-v\) 的数即可

#include<bits/stdc++.h>
using namespace std;
#define inl inline
#define eb emplace_back
#define endl '\n'
#define pii pair<int,int>
#define mkp make_pair
#define fi first 
#define se second 
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
#define int long long
const int N = 40 + 5;

int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

vector<int> vec[2];
int n , m , a[N] , ans;

void dfs ( int rt , int sum , int st , int ed )
{
	if ( sum > m ) return;
	if ( st > ed ) return vec[rt].eb(sum) , void();
	dfs ( rt , sum + a[st] , st + 1 , ed );
	dfs ( rt , sum , st + 1 , ed );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	int mid = n >> 1;
	dfs ( 0 , 0 , 1 , mid );
	dfs ( 1 , 0 , mid + 1 , n );
	sort ( vec[1].begin() , vec[1].end() );
	for ( auto v : vec[0] )
		ans = max ( ans , vec[1][upper_bound ( vec[1].begin() , vec[1].end() , m - v ) - vec[1].begin() - 1] + v );
	cout << ans << endl;
	return 0;
}	

P3067 [USACO12OPEN] Balanced Cow Subsets G

显然正常枚举会 \(T\) (\(3^n>1e9\)) 那么我们考虑折半搜索来降低复杂度

显然我们注意的是两个子集的差而不是两个子集都选了哪些数

那么我们可以为每一个 \(a\)\(b\) 的差值 (用 \(map\) 实现) 维护一个 \(vector\) 并推入选子集的状态集合 (用状压实现)

然后对于前后两半用 \(map\) 查询权值并组合即可

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define print(x) cout<<#x<<' '<<x<<endl
const int N = 2e6 + 5;
const int inf = 0x3f3f3f3f;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , mid , a[N] , tot , ans , vis[N];

map<int,int> mp;
vector<int> vec[N];


void dfs1 ( int st , int ed , int sum , int state )
{
	if ( st > ed ) 
	{
		if ( !mp.count(sum) ) mp[sum] = ++tot;
		vec[mp[sum]].eb(state); return;
	}
	dfs1 ( st + 1 , ed , sum , state );
	dfs1 ( st + 1 , ed , sum + a[st] , state | ( 1 << ( st - 1 ) ) );
	dfs1 ( st + 1 , ed , sum - a[st] , state | ( 1 << ( st - 1 ) ) );
}

void dfs2 ( int st , int ed , int sum , int state )
{
	if ( st > ed ) { for ( auto v : vec[mp[-sum]] ) vis[v|state] = 1; return; }
	dfs2 ( st + 1 , ed , sum , state );
	dfs2 ( st + 1 , ed , sum + a[st] , state | ( 1 << ( st - 1 ) ) );
	dfs2 ( st + 1 , ed , sum - a[st] , state | ( 1 << ( st - 1 ) ) );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , mid = n >> 1;
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	dfs1 ( 1 , mid , 0 , 0 );	
	dfs2 ( mid + 1 , n , 0 , 0 );
	for ( int i = 1 ; i < ( 1 << n ) ; i ++ ) ans += vis[i];
	cout << ans << endl;
	return 0;
}

记忆化搜索

P1514 [NOIP2010 提高组] 引水入城

记忆化搜索好题

显然第一个很好解决 深搜打 \(vis\) 标记即可解决

对于第二个问题 考虑一个结论: 最后第 \(n\) 行的一段连续区间一定可以只被一个蓄水站覆盖 因为如果两个蓄水站有交集 那么第一个蓄水站一定可以通过走到第二个蓄水站上来覆盖两个蓄水站能覆盖到的区间

那么我们在回溯的时候可以记录 \(l[i][j]\) 表示从 \((i,j)\) 点出发 能达到的最左边界 \(r[i][j]\) 同理

那么我们记录 \(lst\) 表示最后一行的区间起始点 遍历每个区间并更新最右节点 相当于是一个蓄水站的最大覆盖右端点

再将 \(lst\) 右移并统计即可

注意 只有出点的高度小于当前点的高度的时候才能继续向下搜索

#include <bits/stdc++.h>
using namespace std;
#define inl inline
const int N = 500 + 5;
const int inf = 0x3f3f3f3f;
int dx[4] = { 0 , 1 , 0 , -1 };
int dy[4] = { 1 , 0 , -1 , 0 };

inl int read ()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar (); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar (); }
	return x * f;
}

int l[N][N] , r[N][N] , n , m , a[N][N] , vis[N][N] , ans;
void dfs ( int x , int y )
{
	vis[x][y] = 1;
	for ( int i = 0 ; i < 4 ; i ++ )
	{
		int tx = x + dx[i] , ty = y + dy[i];
		if ( 1 <= tx && tx <= n && 1 <= ty && ty <= m && a[x][y] > a[tx][ty] )
		{
			if ( !vis[tx][ty] ) dfs ( tx , ty );
			l[x][y] = min ( l[x][y] , l[tx][ty] );
			r[x][y] = max ( r[x][y] , r[tx][ty] );
		}
	}
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	memset ( l , inf , sizeof l );
	n = read() , m = read();
	for ( int i = 1 ; i <= m ; i ++ ) l[n][i] = r[n][i] = i;
	for ( int i = 1 ; i <= n ; i ++ ) for ( int j = 1 ; j <= m ; j ++ ) a[i][j] = read();
	for ( int i = 1 ; i <= m ; i ++ ) if ( !vis[1][i] ) dfs ( 1 , i );
	int flag = 0 , tot = 0;
	for ( int i = 1 ; i <= m ; i ++ ) if ( !vis[n][i] ) ++ tot , flag = 1;
	if ( flag ) return cout << 0 << endl << tot << endl , 0;
	int lft = 1;
	while ( lft <= m )
	{
		int maxx = 0;
		for ( int i = 1 ; i <= m ; i ++ ) 
			if ( l[1][i] <= lft ) maxx = max ( maxx , r[1][i] );
		lft = maxx + 1;
		++ ans;
	}
	cout << 1 << endl << ans << endl;
	return 0;
}

IDA*

基本特征是深搜+估价函数

适用于步数不多但是状态非常多的时候

我们通过枚举步数 对于当前最优情况下能到达哪些节点 如果现在的步数在最优策略下也一定无法到达 那么剪枝即可

P2324 [SCOI2005] 骑士精神

相当于是枚举可能的步数 对于每一个步数进行 \(check\) 即可

对于每一个状态 可以想一下它到终点的最优步数即为:当前矩阵和目标矩阵的不同点数

注意初始矩阵要设对()

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
const int N = 1e3 + 10;
int dx[8] = { 1 , 1 , -1 , -1 , 2 , 2 , -2 , -2 };
int dy[8] = { 2 , -2 , 2 , -2 , 1 , -1 , 1 , -1 };
int goal[6][6] = 
{
	{ 0 , 0 , 0 , 0 , 0 , 0 } ,
	{ 0 , 1 , 1 , 1 , 1 , 1 } ,
	{ 0 , 0 , 1 , 1 , 1 , 1 } ,
	{ 0 , 0 , 0 , 2 , 1 , 1 } ,
	{ 0 , 0 , 0 , 0 , 0 , 1 } ,
	{ 0 , 0 , 0 , 0 , 0 , 0 } ,
};

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , a[N][N] , flag , sx , sy;
char ch;

int check()
{
	int cnt = 0;
	for ( int i = 1 ; i <= 5 ; i ++ )
		for ( int j = 1 ; j <= 5 ; j ++ )
			cnt += ( a[i][j] != goal[i][j] );
	return cnt;
}

void dfs ( int stp , int x , int y , int lim )
{
	// cout << stp << ' ' << x << ' ' << y << ' ' << lim << endl;
	if ( !check() ) return flag = 1 , void();
	for ( int i = 0 ; i < 8 ; i ++ )
	{
		int tx = x + dx[i] , ty = y + dy[i];
		if ( 1 <= tx && tx <= 5 && 1 <= ty && ty <= 5 )
		{
			swap ( a[x][y] , a[tx][ty] );
			if ( stp + check() <= lim ) dfs ( stp + 1 , tx , ty , lim );
			swap ( a[x][y] , a[tx][ty] );
		}
	}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	int T = read();
	while ( T -- )
	{
		for ( int i = 1 ; i <= 5 ; i ++ )
			for ( int j = 1 ; j <= 5 ; j ++ )
			{
				cin >> ch;
				if ( ch == '0' ) a[i][j] = 0;
				else if ( ch == '1' ) a[i][j] = 1;
				else a[i][j] = 2 , sx = i , sy = j;
			}
		if ( !check() ) { cout << 0 << endl; continue; } 
		flag = 0;
		for ( int i = 1 ; i <= 15 ; i ++ )
		{
			dfs ( 0 , sx , sy , i );
			if ( flag ) { cout << i << endl; break; }
		}
		if ( !flag ) cout << -1 << endl;
	}
	return 0;
}

编辑书稿 Editing a Book

定义名词"断点" \(k\) 表示序列中不符合连续递增的点之间的间隔数目

例如:

12|56|34|78 (这里"|"是断点)

那么我们一次操作最多可以消除 \(3\) 个断点 也就是将 "34" 挪到 "56" 前 可以证明所有操作都不可能大于三步(但有可能小于三步) 这样我们就可以得到乐观估价函数: \(h(x)=k/3\)

然后我们枚举每一次的操作 首先枚举操作长度 其次枚举起始节点和终止节点 再枚举需要复制到的位置即可

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define inl inline
#define print(x) cout<<#x<<' '<<x<<endl
const int N = 100 + 5;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , ans , a[N] , temp[N][N] , flag , cases;

int check ()
{
	int cnt = 0;
	for ( int i = 2 ; i <= n ; i ++ ) cnt += ( a[i-1] != a[i] - 1 );
	return ( cnt + 2 ) / 3;
}

void dfs ( int stp , int lim )
{
	if ( !check() ) return flag = 1 , void();
	for ( int len = 1 ; len <= n ; len ++ )
		for ( int l = 1 , r = l + len - 1 ; r <= n ; l ++ , r ++ )
			for ( int k = r + 1 ; k <= n ; k ++ )
			{
				for ( int s = 1 ; s <= n ; s ++ ) temp[stp][s] = a[s];

				int i = l;
				for ( int j = r + 1 ; j <= k ; i ++ , j ++ ) a[i] = temp[stp][j];
				for ( int j = l ; j <= r ; i ++ , j ++ ) a[i] = temp[stp][j];
				if ( check() + stp <= lim ) dfs ( stp + 1 , lim );

				for ( int s = 1 ; s <= n ; s ++ ) a[s] = temp[stp][s];

				if ( flag ) return;
			}
}

signed main ()
{
	// ios::sync_with_stdio(false);
	// cin.tie(0) , cout.tie(0);
	while ( 1 )
	{
		n = read(); if ( n == 0 ) break;
		for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
		flag = 0;
		for ( int i = 0 ; i <= n ; i ++ )
		{
			dfs ( 1 , i );
			if ( flag ) { cout << "Case " << ++cases << ": " << i << endl; break; }
		}
	}
	return 0;
}

P2534 [AHOI2012] 铁盘整理

\(IDA*\) 模板题

估价函数显然是:两个之间是否完美相邻(完美相邻即为上下两个盘在最优序列里也是相邻的 相当于是在离散化之后相差 \(1\) )

我们需要将 \(n+1\) 这个点设置成 \(n+1\) 因为如果需要反转 \([1,n]\) 那么 \(n\)\(n+1\) 没有相差不是 \(1\) 这样就可以起到特判的作用

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define print(x) cout<<#x<<' '<<x<<endl
const int N = 2e6 + 5;
const int inf = 0x3f3f3f3f;

int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , a[N] , flag;

vector<int> lsh;

int check ()
{
	int cnt = 0;
	for ( int i = 1 ; i <= n ; i ++ ) cnt += ( abs ( a[i] - a[i+1] ) != 1 );
	return cnt;
}

void dfs ( int stp , int lim )
{
	if ( !check() ) cout << lim << endl , exit(0);
	for ( int i = 1 ; i <= n ; i ++ ) 
	{
		reverse ( a + 1 , a + i + 1 );
		if ( stp + check() <= lim ) dfs ( stp + 1 , lim );
		reverse ( a + 1 , a + i + 1 );
	}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , lsh.eb(a[i]);
	sort ( lsh.begin() , lsh.end() );
	lsh.erase ( unique ( lsh.begin() , lsh.end() ) , lsh.end() );
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = lower_bound ( lsh.begin() , lsh.end() , a[i] ) - lsh.begin() + 1;
	a[n+1] = n + 1;
	for ( int i = 0 ; ; i ++ ) dfs ( 1 , i );
	return 0;
}

A*

相当于广搜+估价函数

P1379 八数码难题

\(ida*\) 没有本质差别 也是以当前状态和目标状态进行比对

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define print(x) cout<<#x<<' '<<x<<endl
const int N = 2500 + 5;
const int inf = 0x3f3f3f3f3f3f3f3f;
int dx[4] = { 0 , 1 , -1 , 0 };
int dy[4] = { 1 , 0 , 0 , -1 };
int goal[4][4] = 
{ 
	{ 0 , 0 , 0 , 0 } , 
	{ 0 , 1 , 2 , 3 } ,
	{ 0 , 8 , 0 , 4 } , 
	{ 0 , 7 , 6 , 5 } ,
};
 
int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , k , sx , sy , a[N][N] , ans , flag;
char ch;

int check()
{
	int cnt = 0;
	for ( int i = 1 ; i <= 3 ; i ++ )
		for ( int j = 1 ; j <= 3 ; j ++ )
			cnt += ( goal[i][j] != a[i][j] );
	return cnt == 0 ? 0 : cnt - 1;
}

void dfs ( int stp , int x , int y , int lim , int pre )
{
	if ( !check() ) return flag = 1 , void();
	for ( int i = 0 ; i < 4 ; i ++ )
	{
		int tx = x + dx[i] , ty = y + dy[i];
		if ( 1 <= tx && tx <= 3 && 1 <= ty && ty <= 3 && pre + i != 3 )
		{
			swap ( a[x][y] , a[tx][ty] );
			if ( check() + stp <= lim ) dfs ( stp + 1 , tx , ty , lim , i );
			swap ( a[tx][ty] , a[x][y] );
			if ( flag ) return;
		}
	}
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	for ( int i = 1 ; i <= 3 ; i ++ )
		for ( int j = 1 ; j <= 3 ; j ++ )
		{
			cin >> ch , a[i][j] = ch - '0';
			if ( !a[i][j] ) sx = i , sy = j;
		}
	for ( int i = 0 ;  ; i ++ )
	{
		dfs ( 1 , sx , sy , i , -1 );
		if ( flag ) return cout << i << endl , 0;
	}
	return 0;
}

P2901 [USACO08MAR] Cow Jogging G

\(dij+A*\) \(k\) 短路模板题

我们首先需要从终点搜出到每一个点的 \(dis[u]\) 方便估价函数使用

其次我们再从起点跑一次广搜 用优先队列每次扩展 当前 \(dis\) 加上到终点的最优 \(dis\) 最小的点 到一次终点输出一次距离即可

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define getchar() cin.get()
#define eb emplace_back
#define pii pair<int,int>
#define fi first
#define se second
const int N = 1e3 + 5;
const int inf = 0x3f3f3f3f;

int read()
{
	int f = 1 , x = 0;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}

int n , m , k , dis[N];

vector<pii> e[N] , ee[N];
inl void add ( int u , int v , int w ) { e[u].eb(v,w); }
inl void adde ( int u , int v , int w ) { ee[u].eb(v,w); }

struct node { int id , val; friend bool operator < ( const node &a , const node &b ) { return a.val > b.val; } };
priority_queue<node> q;
void dij ()
{
	memset ( dis , inf , sizeof dis );
	q.push ( (node) { 1 , dis[1] = 0 } );
	while ( !q.empty() )
	{
		int u = q.top().id , ff = q.top().val; q.pop();
		if ( ff != dis[u] ) continue;
		for ( auto p : e[u] )
		{
			int v = p.fi , w = p.se;
			if ( dis[v] > dis[u] + w )
			{
				dis[v] = dis[u] + w;
				q.push ( (node) { v , dis[v] } );
			}
		}
	}
}


struct edge { int id , val; friend bool operator < ( const edge &a , const edge &b ) { return a.val + dis[a.id] > b.val + dis[b.id]; } };
priority_queue<edge> qq;
void A_star ( int &cnt )
{
	qq.push ( { n , 0 } );
	while ( !qq.empty() )
	{
		int u = qq.top().id , ff = qq.top().val; qq.pop();
		if ( u == 1 )
		{
			cout << ff << endl;
			if ( (--cnt) == 0 ) return;
		}
		for ( auto p : ee[u] )
		{
			int v = p.fi , w = p.se;
			qq.push ( { v , ff + w } );
		}
	}
}


signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read() , k = read();
	for ( int i = 1 , u , v , w ; i <= m ; i ++ ) u = read() , v = read() , w = read() , adde ( u , v , w ) , add ( v , u , w );
	dij();
	A_star(k);
	while ( k -- ) cout << -1 << endl;
	return 0;
}
posted @ 2023-10-04 06:19  Echo_Long  阅读(6)  评论(0编辑  收藏  举报