usaco 1.4 枚举&搜索

本文介绍了如何使用枚举和搜索解决USACO中的算法问题,包括利用位操作简化搜索过程,以及在不同题目中应用枚举策略,如避免无效操作和优化搜索空间。通过具体例题,如'Arithmetic Progressions'和'Mother's Milk',展示了如何有效地进行枚举和剪枝,提高算法效率。
摘要由CSDN通过智能技术生成

the clocks  这道题题面上是一个搜索类型的题,有9个时钟,每个时钟有4个状态,现在有9种操作,每种操作可以改变某些时钟的状态,问什么时候9个时钟都到状态0,一开始没有多想就写了个BFS,但是效率太低了,主要是深搜没有状态判重。。比如同一个操作最多就做3次,第四次做完以后就相当于没做,BFS没考虑这个的话就会陷入低效。

有了每种情况都只有3次,另外一种思路就是枚举,用到二进制位操作,程序就变得非常简洁:

对于一开始的时钟状态可以用一个二进制数表示 如 000 000 000 000 000 000 001 001 011 九个时钟每个时钟由3个bit位就可以表示。

九种操作同理用二进制表示后, 令set = 100 100 100 100 100 100 100 100 100 100 ,, (a + b) & set就是操作后的结果

操作序列同样用二进制数表示 每种操作1-3次,可以用2个bit表示那 一个 2^18的数就可以枚举完所有操作可能



/*
ID: zhangw31
PROG: clocks
LANG: C++
*/

#include <iostream>
#include <fstream>
using namespace std;

const int trans[9] = {18911232, 19136512, 2363904, 16810048, 2134536, 262657, 36936, 73, 4617};
int clears = 57521883;
int s, a, tmp, t,l;
int minl = 999999;

int main() {
	ifstream fin("clocks.in");
	ofstream fout("clocks.out");
	for (int i = 8; i >= 0; i--) {
		fin >> a;
		s |= (a/3 % 4) << (3 * i);
	}
	for (int i = 0; i < 1048576; i++) {
		tmp = s;
		l = 0;
		for (int j = 0; j < 9; j++) {
			t = i>>2*j & 3;
			tmp = (tmp + trans[j]*t) & clears;
			l += t;
		}
		if (tmp == 0 && l < minl) {
			minl = l;
			a = i;
		}
	}
	for (int i = 0; i < 9; i++) {
		for (int j = 0; j < (a>>i*2&3) ; j++) {
			fout << i + 1;
			if (--minl) fout << " ";
			else fout << endl;
		}
	}
}/*


Arithmetic Progressions 是一个枚举题,给定N,M,要找到N阶的p^2+q^2(p,q均小于M)集合里的等差数列

思路1 开一个一维数组用于模拟某个数是否为合格的数   + 三重for:第一层枚举起点(M^2),第二层枚举等差数列的d值(M^2),第三层枚举N;

复杂度为M^4*N,5秒内出不了结果。。。。

修正:首先枚举起点和d值的时候并不用M^2仅用枚举set里的元素和元素之间的差即可,还有一个重要的剪枝:当num[i] + (n-1)*d > max时,就不必进行N循环

/*
ID: zhangw31	
PROG: ariprog
LANG: C++
*/

#include <iostream>
#include <fstream>
#include <queue>
#include <set>
using namespace std;

int N, M;
bool isnum[250005];
int num[125005];
int count1;
ifstream fin("ariprog.in");
ofstream fout("ariprog.out");

struct Mystruct {
	int start;
	int progress;
	Mystruct (int s, int p) : start(s), progress(p) {}
	bool operator <(const Mystruct& b) const {
		if (progress != b.progress) return progress > b.progress;
		else return start > b.start;
	}
};

priority_queue<Mystruct> q;
set<int> s;

int main() 
{ 
	fin >> N >> M;
	for (int i = 0; i <= M; i++) {
		for (int j = 0; j <= M; j++) {
			isnum[i*i + j*j] = true;
		}
	}
	for (int i = 0; i <= 2*M*M; i++) {
		if (isnum[i]) {
			num[count1++] = i;
		}
	}
	for (int i = 0; i < count1; i++) {
		for (int j = i+1; j < count1; j++) {
			bool isok = true;
			int d = num[j] - num[i];
			if (num[i] + (N-1) * d > num[count1-1]) {
				isok = false;
			}
			else for (int k = 2; k < N; k++) {
				if (!isnum[num[i] + k*d]) {  
					isok = false;
					break;
				}
			}
			if (isok) {
				q.push(Mystruct(num[i], d));
			}
		}
	}
	if (q.empty()) {
		fout << "NONE" << endl;
	}
	while(!q.empty()) {
		Mystruct tmp = q.top(); q.pop();
		fout << tmp.start << " " << tmp.progress << endl;
	}

}
			


Mother's Milk 三个水杯倒水的问题,abc三个水杯,abc各有一个乘水上限,问当a杯为0时,c中的水量:

一开始写了个struct+queue广搜,但是效果不好,主要是在判重方面,后面改为深搜就简洁了许多dfs(a,b,c)分别代表abc的水量,那么可以有的操作就只有

ab, ac, bc, ba, ca, cb六种,再用vis[i][j] 作为判重abc三个量,实际上是二维的变量。

/*
ID: zhangw31
PROG: milk3
LANG: C++
*/

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

ifstream fin("milk3.in");
ofstream fout("milk3.out");
bool visit[25][25];
bool state[25];
int maxa, maxb, maxc, count1;

void dfs(int a, int b, int c) {
	if (visit[a][b]) return;
	visit[a][b] = true;
	if (a == 0) { state[c] = true; count1++;}
	if (a + c >= maxc)  dfs(a-maxc+c, b, maxc);
	if (a + c < maxc)  dfs(0, b, c+a);
	if (a + b >= maxb)  dfs(a-maxb+b, maxb, c);
	if (a + b < maxb)  dfs(0, b+a, c);
	if (b + c >= maxc)  dfs(a, b-maxc+c, maxc);
	if (b + c < maxc)  dfs(a, 0, c+b);
	if (b + a >= maxa)  dfs(maxa, b-maxa+a, c);
	if (b + a < maxa)  dfs(a+b, 0, c);
	if (c + a >= maxa)  dfs(maxa, b, c-maxa+a);
	if (c + a < maxa)  dfs(a+c, b, 0);
	if (b + c >= maxb)  dfs(a, maxb, c-maxb+b);
	if (b + c < maxb)  dfs(a, b+c, 0);
}

int main()
{
	string ans;
	fin >> maxa >> maxb >> maxc;
	dfs(0, 0, maxc);
	for (int i = 0; i <= 20; i++) {
		if (state[i]) {
			fout << i;
			if (--count1)
				fout << " ";
		}
	}
	fout << endl;
}

packrec 这道题目依旧是比较暴力的一个枚举题,枚举4个矩形组合但不重合的最小面积,实际上题目已经给出了组合的所有形式,那么要做的首要工作就是如何排列,和那几个旋转,下面是一种递归排列和递归旋转的方式,当然用二进制枚举排列和旋转也可以

void rotate(int k) 
{
	if (k == 4) calcu();
	else 
	{
		rotate(k + 1);
		swap(a[k].x, a[k].y);
		rotate(k + 1);
		swap(a[k].x, a[k].y);
	}
}

void permutation(int level) 
{
	if (level == 4) rotate(0);
	else {
		for (int i = level; i < 4; i++) {
			swap(a[level], a[i]);
			permutation(level+1);
			swap(a[level], a[i]);
		}
	}
}



1.4over

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值