搜索进阶

写在前面:

推歌

《龙卷风》

爱像一阵风 吹完它就走
这样的节奏 谁都无可奈何
没有你以后 我灵魂失控
黑云在降落 我被它拖着走
静静悄悄默默离开
陷入了危险边缘 Baby
我的世界已狂风暴雨
Wu 爱情来的太快 就像龙卷风
离不开暴风圈 来不及逃
我不能再想我 不能再想
我不 我不 我不能
爱情走的太快 就像龙卷风
不能承受 我已无处可躲
我不要再想 我不要再想
我不 我不 我不要再想你
不知不觉 你已经离开我
不知不觉 我跟了这节奏
后知后觉 又过了一个秋
后知后觉 我该好好生活
静静悄悄默默离开
陷入了危险边缘 Baby
我的世界已狂风暴雨
Wu 爱情来的太快 就像龙卷风
离不开暴风圈 来不及逃
我不能再想 我不能再想
我不 我不 我不能
爱情走的太快 就像龙卷风
不能承受 我已无处可躲
我不要再想 我不要再想
我不 我不 我不要再想你
爱情来的太快 就像龙卷风
离不开暴风圈 来不及逃
我不能再想 我不能再想
我不 我不 我不能
爱情走的太快 就像龙卷风
不能承受 我已无处可躲
我不要再想 我不要再想
我不 我不 我不要再想你
不知不觉 你已经离开我
不知不觉 我跟了这节奏
后知后觉 又过了一个秋
后知后觉 该好好生活
不知不觉 你已经离开我
不知不觉 我跟了这节奏
后知后觉 又过了一个秋
后知后觉 我该好好生活
不知不觉 你已经离开我
不知不觉 我跟了这节奏
后知后觉 后知后觉

话:

是的是的别的专题肝硬化都没写就只写了搜索专题(别的全不会,全是霍的,肝硬化也就只能写写暴搜了),上次写好像是NOIP2025专题-搜索&模拟(超绝烂尾版)欸,希望这篇不要烂尾喵!(显然假的


章节 1. 折半搜索

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

Solve:

首先通过标签&章节名可知需要用到折半搜索 \(meet~in~the~middle\)
首先发现数据范围 \(n\) 很小于是想到暴搜,但是电风扇\(DFS\) )的复杂度是 \(O(2^n)\)\(n\) 能到 \(40\) 显然会 \(TLE\) ,但是可以发现 \(2^{20}\) 可以做,于是 \(meet~in~the~middle\) ,然后就过了。
统计答案的时候可以用 \(map\) 也可以开两个数组存然后其中一个数组排序之后找。
然后真的没了,就纯板子,肝硬化都不知道自己为什么写了这么多字喵。


Code:

#include <bits/stdc++.h>
using namespace std;
const int __ = 44, _ = (1 << 20) + 10;
int n, t, numa, numb;
long long a[__], ans, sa[_], sb[_], m;
inline void dfs(int nw, long long sum, long long s[], int & num){
	if(sum > m){
		return ;
	}
	if(nw > t){
		s[++ num] = sum;
		return ;
	}
	dfs(nw + 1, sum + a[nw], s, num);
	dfs(nw + 1, sum		   , s, num);
	return ;
}
int main(){
	scanf("%d%lld", & n, & m);
	for(int i = 1; i <= n; i ++){
		scanf("%lld", & a[i]);
	}
	t = n >> 1;
	dfs(1, 0, sa, numa);
	t = n;
	dfs((n >> 1) + 1, 0, sb, numb);
	std::sort(sb + 1, sb + numb + 1);
	for(int i = 1; i <= numa; i ++){
		ans += upper_bound(sb + 1, sb + numb + 1, m - sa[i]) - sb - 1;
	}
	printf("%lld", ans);
	return 0;
}

T2 [NOI2001] 方程的解数

Solve:

数学¿
我不会。
于是暴搜,朴素地枚举 \(x_i\) ,发现复杂度 \(O(m^n)\) , 听到了 \(TLE\) の回响!
于是直接折半搜。
知道怎么搜吗,知道为什么搜吗,知道怎么统计答案吗?
对不起,但是这只是一个绿题欸………
但是肝硬化泰蒻了,于是一番注意发现柿子可以移项变成这样:

\[\sum_{i=1}^{\frac{n}{2}}k_{i}x_i^{p_i} = -\sum_{i=\frac{n}{2} + 1}^{n}k_{i}x_i^{p_i} \]

于是就是折半搜板子。


Code:

#include <bits/stdc++.h>
using namespace std;
const int __ = 11, _ = 3380010;
int n, t, numa, numb, m;
long long k[__], p[__], ans, sa[_], sb[_];
inline long long ksm(long long a, long long b){
	long long as = 1;
	while(b){
		if(b & 1){
			as *= a;
		}
		a *= a;
		b >>= 1;
	}
	return as;
}
inline void dfs(int nw, long long sum, long long s[], int & num){
	if(nw > t){
		s[++ num] = sum;
		return ;
	}
	for(int i = 1; i <= m; i ++){
		dfs(nw + 1, sum + k[nw] * ksm(i, p[nw]), s, num);
	}
	return ;
}
int main(){
	scanf("%d%d", & n, & m);
	for(int i = 1; i <= n; i ++){
		scanf("%lld%lld", & k[i], & p[i]);
	}
	t = n >> 1;
	dfs(1, 0, sa, numa);
	t = n;
	dfs((n >> 1) + 1, 0, sb, numb);
	std::sort(sa + 1, sa + numa + 1);
	std::sort(sb + 1, sb + numb + 1);
	for(int i = 1, j = numb; i <= numa && j; i ++){
		while(sa[i] + sb[j] > 0 && j){
			j --;
		}
		int x = 0, y = 1;
		for(int l = j; sa[i] + sb[l] == 0 && l; l --){
			x ++;
		}
		while(sa[i] + sb[j] == 0 && sa[i] == sa[i] + 1 && i < numa){
			i ++;
			y ++;
		}
		ans += x * y;
	}
	printf("%lld", ans);
	return 0;
}

T3 [USACO12OPEN] Balanced Cow Subsets G

哈哈我是高一选手刚刚学了集合一调考出了年级前100的坏成绩于是我………不会做。
首先我们可以根据文化课经验得知一个有 \(n\) 个元素的集合有 \(2^n\) 个子集,但是 \(O(2^n)\)\(T\) ,于是我们考虑折半搜,将最终合法的方案数减一(减去空集)除以二(一些牛在集合 \(A\) ,另一些牛在集合 \(B\) ,与刚刚在集合 \(B\) 的牛在集合 \(A\) ,刚刚在集合 \(A\) 的牛在集合 \(B\) 本质相同但会被看成两种方案)。
你说得不对,因为你 \(WA~~38pts\) 寄:提交记录
于是通过题目讨论版可以发现:我们要求的其实是“能分成两个和相同的子集的集合数”,而不是“和相等的两个集合对的对数”,我们刚才的做法明显会使答案偏大,应该每次搜索都记录一下当前选的牛的编号集合,具体实现可以状压,二进制下的每一位值为 \(1\) 代表选这头牛, \(0\) 代表没有,然后去重,具体实现看代码(真的会有比肝硬化还蒻还不会写的人吗¿【大悲】)。


Code:

#include <bits/stdc++.h>
const int __ = 22, _ = 2e6 + 10;
int n, numa, numb, t, a[__], ans, ca[_], cb[_], as[_], num;
std::unordered_map<int, int> mp;
std::vector<int> c[_];
inline void dfsa(int nw, int cha, int zhuang){
	if(nw > t){
		if(! mp[cha]){
			mp[cha] = ++ num;
		}
		c[mp[cha]]. emplace_back(zhuang);
		return ;
	}
	dfsa(nw + 1, cha + a[nw], zhuang | (1 << (nw - 1)));
	dfsa(nw + 1, cha - a[nw], zhuang | (1 << (nw - 1)));
	dfsa(nw + 1, cha		, zhuang				  );
	return ;
}
inline void dfsb(int nw, int cha, int zhuang){
	if(nw > t){
		if(mp[cha]){
			for(auto h : c[mp[cha]]){
				as[h | zhuang] = 1;
			}
		}
		return ;
	}
	dfsb(nw + 1, cha + a[nw], zhuang | (1 << (nw - 1)));
	dfsb(nw + 1, cha - a[nw], zhuang | (1 << (nw - 1)));
	dfsb(nw + 1, cha		, zhuang				  );
	return ;
}
int main(){
	scanf("%d", & n);
	for(int i = 1; i <= n; i ++){
		scanf("%d", & a[i]);
	}
	t = n >> 1;
	dfsa(1, 0, 0);
	t = n;
	dfsb((n >> 1) + 1, 0, 0);
	for(int i = 1; i <= (1 << n); i ++){
		ans += as[i];
	}
	printf("%d", ans);
	return 0;
}

T4 [USACO09NOV] Lights G

Solve:

其实正解也有高斯消元+ \(DFS\) ,当然 \(meet~in~the~middle\) 也在标签里qwq。
一看到是开关灯立马应激反应想到高斯消元(万恶的开关问题),但是肝硬化早就把高斯消元写成搞笑消元/搞死小圆(呜呜馒头卡不要亖口牙),于是发现 \(n\) 的范围刚好不够暴搜但折半搜,于是考虑折半搜,首先预处理每个灯切换开关时能受到影响的所有灯,依旧状压处理(开 \(long~long\)!!!),之后搜索的时候如果要切换下一个灯的开关,那么就将当前所有灯的状态异或这个灯预处理出的状态(因为开过的灯再操作一次就会关上,不是吗?),具体实现也是非常不史,见代码就好。
注意细节:
·进行位运算的时候, \(1<<n\) 会爆 \(int\) ,一定要改成 \(1ll<<n\) ;(不开 \(long~long\) 见祖宗啊啊啊啊啊啊啊啊!)
· \(DFS\) 时要异或异或异或,不要写成或,重要的事情说三遍;(不然你样例都过不了!)
· \(ans\)\(min\) 之前赋初值啊赋初值!!!
·没了。


Code:

#include <bits/stdc++.h>
#define int long long
const int __ = 44, _ = 3e5;
int m, x, y, ans = 999999999, t;
long long n, yx[__];
std::unordered_map<long long, int> c;
inline void dfsa(int nw, int bu, long long zhuang){
	if(nw > t){
		if(! c. count(zhuang)){
			c[zhuang] = bu;
		}
		else{
			c[zhuang] = std::min(c[zhuang], bu);
		}
		return ;
	}
	dfsa(nw + 1, bu + 1, zhuang ^ yx[nw]);
	dfsa(nw + 1, bu    , zhuang			);
	return ;
}
inline void dfsb(int nw, int bu, long long zhuang){
	if(bu > ans){
		return ;
	}
	if(nw > t){
		if(c.count(((1ll << n) - 1ll) ^ zhuang)){
			ans = std::min(ans, c[((1ll << n) - 1ll) ^ zhuang] + bu);
		}
		return ;
	}
	dfsb(nw + 1, bu + 1, zhuang ^ yx[nw]);
	dfsb(nw + 1, bu    , zhuang			);
	return ;
}
signed main(){
	yx[1] = 1;
	scanf("%lld%lld", & n, & m);
	for(int i = 2; i <= n; i ++){
		yx[i] = yx[i - 1] << 1ll;
	}
	for(int i = 1; i <= m; i ++){
		scanf("%lld%lld", & x, & y);
		yx[x] |= (1ll << (y - 1));
		yx[y] |= (1ll << (x - 1));
	}
	t = n >> 1;
	dfsa(1			 , 0, 0);
	t = n;
	dfsb((n >> 1) + 1, 0, 0);
	printf("%lld", ans);
	return 0;
}

T5 [abc184_f]Programming Contest


Solve:

我不懂为什么这么唐个绿题板子在 \(T3\) \(T4\) 后面。
几乎就是 \(T1\) 的代码,改成取 \(max\) 就好,然后没了。


Code:

#include <bits/stdc++.h>
using namespace std;
const int __ = 44, _ = 1050000;
int n, t, a[__], numa, numb, ed;
long long sa[_], sb[_], ans;
inline void dfs(int nw, long long sum, long long s[], int & num){
	if(sum > t){
		return ;
	}
	if(nw > ed){
		s[++ num] = sum;
		return ;
	}
	dfs(nw + 1, sum + a[nw], s, num);
	dfs(nw + 1, sum		   , s, num);
	return ;
}
int main(){
	scanf("%d%d", & n, & t);
	for(int i = 1; i <= n; i ++){
		scanf("%d", & a[i]);
	}
	ed = n >> 1;
	dfs(1, 0, sa, numa);
	ed = n;
	dfs((n >> 1) + 1, 0, sb, numb);
	std::sort(sb + 1, sb + numb + 1);
	for(int i = 1; i <= numa; i ++){
		ans = std::max(ans, sa[i] + sb[upper_bound(sb + 1, sb + numb + 1, t - sa[i]) - sb - 1]);
	}
	printf("%lld", ans);
	return 0;
}

章节 2. \(A^*\)

\(A^*\) 其实就是 \(BFS\) 加上一个估价函数 \(h\) ,每次 \(BFS\) 使用优先队列取出当前已知+估计距离最小的点,很可能会使答案更优, \(A^*\) 算法的好坏主要取决于估价函数的优劣(与实际距离越接近越好,但不要大于实际距离),估价函数可能是最短路、曼哈顿距离、欧拉距离……等等,咱题库里的大部分用的是最短路,有 \(0\) 个题用的欧拉距离。
这里给出三个写得清晰易懂的博客(其中两个是私货喵~):
A^*算法详解康托展开神犇%%%笑了 \(inf\) 年才舍得粘全史慎点!!!


T1 【模板】k 短路 / [SDOI2010] 魔法猪学院

Solve:

其实正解是可持久化可并堆,但是我并不会。
但是 \(HZOI\) 的评测机堪比超算,粘过来这道题就是为了让我们用 \(A^*\) 做的,数据非常非常非常水,于是我们关闭大脑,把它当做 \(A^*\) 模板题来写。
首先发现这是一个有向图,并且要输出最多的方案数,于是我们的估价函数可以用最短路,很明显要用最前途的 \(Dijkstra\) 堆优化,此时我们需要建一个反图,跑一遍最短路,并用 \(dis\) 数组存下。
为什么建反图跑最短路?
因为我们的估价函数是估计从当前节点 \(i\)\(n\) 的距离。(跟没说一样)
然后 \(A^*\) 就是个 \(BFS\) ,注意开 \(double\) 以及堆排序的大于号小于号是反的就好。
(这样不带脑子的代码自家 \(OJ\)\(AC\) 但是洛谷不能!!!<因为优先队列东西放太多了太占内存遂 \(MLE\) >)


Code:

#include <bits/stdc++.h>
using namespace std;
const int _ = 2e5 + 10, __ = 5e3 + 10;
int n, m, x, y, to[_], nxt[_], h[__], ot[_], txn[_], u[__], tot, oto, ans;
double all, z, len[_], dis[__], nel[_];
bool v[__];
inline void dda(int x, int y, double z){//这些史一样的变量名都说明它是建的是反图,不明显吗?
	ot[++ oto] = y;
	txn[oto] = u[x];
	u[x] = oto;
	nel[oto] = z;
	return ;
}
inline void add(int x, int y, double z){
	to[++ tot] = y;
	nxt[tot] = h[x];
	h[x] = tot;
	len[tot] = z;
	return ;
}
struct hhh{
	int num;
	double len;
	inline bool operator < (const hhh & x)const{
		return len > x. len;
	}
};
struct rain{
	int num;
	double len;
	inline bool operator < (const rain & x)const{
		return len + dis[num] > x. len + dis[x. num];
	}
};
inline void dijkstra(){
	std::priority_queue<hhh> q;
	for(int i = 1; i <= n; i ++){
		dis[i] = 999999999;
	}
	dis[n] = 0;
	q. push({n, 0});
	while(q. size()){
		hhh x = q. top();
		q. pop();
		if(v[x. num]){
			continue;
		}
		v[x. num] = 1;
		for(int i = u[x. num]; i; i = txn[i]){
			y = ot[i];
			if(dis[y] > dis[x. num] + nel[i]){
				dis[y] = dis[x. num] + nel[i];
				if(! v[y]){
					q. push({y, dis[y]});
				}
			}
		}
	}
	return ;
}
inline void Astar(){
	std::priority_queue<rain> q;
	q. push({1, 0});
	while(q. size()){
		rain x = q. top();
		q. pop();
		if(x. num == n){
			if(all < x. len){
				return ;
			}
			all -= x. len;
			ans ++;
		}
		for(int i = h[x. num]; i; i = nxt[i]){
			y = to[i];
			q. push({y, x. len + len[i]});
		}
	}
	return ;
}
int main(){
	scanf("%d%d%lf", & n, & m, & all); 
	for(int i = 1; i <= m; i ++){
		scanf("%d%d%lf", & x, & y, & z);
		add(x, y, z);
		dda(y, x, z);
	}
	dijkstra();
	Astar();
	printf("%d", ans);
	return 0;
}

T2 [Tyvj Feb11] GF打dota


Solve:

其实我们完全可以在原题的数据范围内 \(Hack\)\(A^*\) 做法使其 \(MLE\) (还是因为优先队列东西放太多了太占内存),但是数据过水于是也成了 \(A^*\) 的模板题。
而且这个题是个无向图,于是我们正常建边,依旧是跑一遍最短路处理估价再跑 \(A^*\) ;依旧注意堆的大于号小于号是反的。
这里给出zxk大蛇提供的一组 \(Hack\)


Code:

#include <bits/stdc++.h>
using namespace std;
const int _ = 50010;
int n, m, x, y, z, dis[_], to[_ << 1], nxt[_ << 1], h[_], tot, len[_ << 1];
bool v[_];
inline void add(int x, int y, int z){
	to[++ tot] = y;
	nxt[tot] = h[x];
	h[x] = tot;
	len[tot] = z;
	return ;
}
struct hhh{
	int num, len;
	inline bool operator < (const hhh & x)const{
		return len > x. len;
	}
};
struct rain{
	int num, len;
	inline bool operator < (const rain & x)const{
		return len + dis[num] > x. len + dis[x. num];
	}
};
inline void dijkstra(){
	std::priority_queue<hhh> q;
	for(int i = 1; i <= n; i ++){
		dis[i] = 999999999;
	}
	dis[1] = 0;
	q. push({1, 0});
	while(q. size()){
		hhh x = q. top();
		q. pop();
		if(v[x. num]){
			continue;
		}
		v[x. num] = 1;
		for(int i = h[x. num]; i; i = nxt[i]){
			y = to[i];
			if(dis[y] > dis[x. num] + len[i]){
				dis[y] = dis[x. num] + len[i];
				if(! v[y]){
					q. push({y, dis[y]});
				}
			}
		}
	}
	return ;
}
inline void Astar(){
	std::priority_queue<rain> q;
	q. push({n, 0});
	while(q. size()){
		rain x = q. top();
		q. pop();
		if(x. num == 1){
			if(x. len > dis[n]){
				printf("%d", x. len);
				return ;
			}
		}
		for(int i = h[x. num]; i; i = nxt[i]){
			y = to[i];
			q. push({y, x. len + len[i]});
		}
	}
	return ;
}
int main(){
	scanf("%d%d", & n, & m);
	for(int i = 1; i <= m; i ++){
		scanf("%d%d%d", & x, & y, & z);
		add(x, y, z);
		add(y, x, z);
	}
	scanf("%d", & m);
	dijkstra();
	if(! m){
		printf("%d", dis[n]);
		return 0;
	}
	Astar();
	return 0;
}

T3 牛跑步

Solve:

这回标签里终于有 \(A^*\) 了可喜可贺可喜可贺!!!
我们依旧发现,因为 \(Bessie\) 只能从高往低跑,于是又是有向图,依旧像 \(T1\) 一样建反图跑最短路再 \(A^*\) 即可。


Code:

#include <bits/stdc++.h>
using namespace std;
const int _ = 1e6 + 10;
int k, z, dis[_], n, m, x, y, to[_], nxt[_], h[_], ot[_], txn[_], u[_], tot, oto, len[_], nel[_];
bool v[_];
inline void dda(int x, int y, int z){
	ot[++ oto] = y;
	txn[oto] = u[x];
	u[x] = oto;
	nel[oto] = z;
	return ;
}
inline void add(int x, int y, int z){
	to[++ tot] = y;
	nxt[tot] = h[x];
	h[x] = tot;
	len[tot] = z;
	return ;
}
struct hhh{
	int num, len;
	bool operator < (const hhh & x)const{
		return len > x. len;
	}
};
struct rain{
	int num, len;
	bool operator < (const rain & x)const{
		return len + dis[num] > x. len + dis[x. num];
	}
};
inline void dijkstra(){
	std::priority_queue<hhh> q;
	for(int i = 1; i <= n; i ++){
		dis[i] = 999999999;
	}
	dis[1] = 0;
	q. push({1, 0});
	while(q. size()){
		hhh x = q. top();
		q. pop();
		if(v[x. num]){
			continue;
		}
		v[x. num] = 1;
		for(int i = u[x. num]; i; i = txn[i]){
			y = ot[i];
			if(dis[y] > dis[x. num] + nel[i]){
				dis[y] = dis[x. num] + nel[i];
				if(! v[y]){
					q. push({y, dis[y]});
				}
			}
		}
	}
	return ;
}
inline void Astar(){
	priority_queue<rain> q;
	q. push({n, 0});
	while(q. size()){
		rain x = q. top();
		q. pop();
		if(x. num == 1){
			printf("%d\n", x. len);
			if(! -- k){
				return ;
			}
		}
		for(int i = h[x. num]; i; i = nxt[i]){
			y = to[i];
			q. push({y, x. len + len[i]});
		}
	}
	return ;
}
int main(){
	scanf("%d%d%d", & n, &m, & k);
	for(int i = 1; i <= m; i ++){
		scanf("%d%d%d", & x, & y, & z);
		add(x, y, z);
		dda(y, x, z);
	}
	dijkstra();
	Astar();
	while(k --){
		printf("-1\n");
	}
	return 0;
}

应学姐要求开始倒序写了喵~(学姐说她觉得红色字体好看)

章节 3. IDA*

赘述先咕。

T4 涂满它(Flood-it)

Solve:

可以用一个数组 \(v\) 预处理出将左上角颜色改成颜色 \(c\) 后能够影响到的所有格子位置(注意是四连通!!!),估价函数就是统计目前从来没影响到的格子的颜色种类总数(这样一定小于等于真实值,是个正确的估价函数),然后每次修改都跑一遍统计能影响到的格子位置的函数,由于肝硬化使用了逆天的 \(bool\) 数组,于是特判略多,实现时可以将已经遍历过的位置换一个值,这样能少吃特判的史。

其实太简略了明天应该会重新写一版。


Code:

#include <bits/stdc++.h>
using namespace std;
const int dx[] = {0, 0, 1, - 1}, dy[] = {1, - 1, 0, 0};
int n, t, a[11][11], xx, yy;
bool v[11][11], cun[6];
inline int h(){
	int as = 0;
	cun[0] = cun[1] = cun[2] = cun[3] = cun[4] = cun[5] = 0;
	for(int i = 1; i <= n; i ++){
		for(int j = 1; j <= n; j ++){
			if(! cun[a[i][j]] && ! v[i][j]){
				as ++;
				cun[a[i][j]] = 1;
			}
		}
	}
	return as;
}
inline void yx(int x = 1, int y = 1, int c = a[1][1]){
	v[x][y] = 1;
	for(int i = 0; i < 4; i ++){
		xx = x + dx[i], yy = y + dy[i];
		if(v[xx][yy] == 1 || xx < 1 || xx > n || yy < 1 || yy > n){
			continue;
		}
		if(a[xx][yy] == c){
			yx(xx, yy, c);
		}
	}
	return ;
}
inline int tu(int c){
	int xin = 0;
	for(int i = 1; i <= n; i ++){
		for(int j = 1; j <= n; j ++){
			if(! v[i][j] && a[i][j] == c && (v[i + 1][j] || v[i - 1][j] || v[i][j + 1] || v[i][j - 1])){//就是这个位置的特判其实完全可以避免! 
				xin ++;
				yx(i, j, c);
			}
		}
	}
	return xin;
}
inline bool dfs(int nw = 0){
	if(nw + h() > t){
		return 0;
	}
	if(! h()){
		return 1;
	}
	bool g[11][11];
	for(int i = 0; i <= 5; i ++){
		memcpy(g, v, sizeof(g));
		if(tu(i) && dfs(nw + 1)){
			return 1;
		}
		memcpy(v, g, sizeof(v));//注意回溯!!! 
	}
	return 0;
}
int main(){
while(cin >> n){
	if(! n){
		return 0;
	}
	for(int i = 1; i <= n; i ++){
		for(int j = 1; j <= n; j ++){
			scanf("%d", & a[i][j]);
			v[i][j] = 0;//别忘多测清空!!! 
		}
	}
	yx();
	t = 0;
	while(! dfs()){
		t ++;
	}
	printf("%d\n", t);
}
	return 0;
}
posted @ 2025-10-16 21:56  养鸡大户肝硬化  阅读(13)  评论(6)    收藏  举报