usaco6.1.3 Cow XOR

本文介绍了一道名为CowXOR的问题,该问题要求从排列好的奶牛中选择连续的一组,使得这些奶牛编号的异或值最大。文章详细解释了如何通过构建Trie树来高效解决此问题,并给出了两种实现方案。

一 原题

Cow XOR
Adrian Vladu -- 2005

Farmer John is stuck with another problem while feeding his cows. All of his N (1 ≤ N ≤ 100,000) cows (numbered 1..N) are lined up in front of the barn, sorted by their rank in their social hierarchy. Cow #1 has the highest rank; cow #N has the least rank. Every cow had additionally been assigned a non-unique integer number in the range 0..(221 - 1).

Help FJ choose which cows will be fed first by selecting a sequence of consecutive cows in the line such that the bitwise "xor" between their assigned numbers has the maximum value. If there are several such sequences, choose the sequence for which its last cow has the highest rank. If there still is a tie, choose the shortest sequence.

TIME LIMIT: 0.5 sec

PROGRAM NAME: cowxor

INPUT FORMAT

  • Line 1: A single integer N
  • Lines 2..N+1: N integers ranging from 0 to 221 - 1, representing the cows' assigned numbers. Line j describes cow of social hierarchy j-1.

SAMPLE INPUT (file cowxor.in)

5
1
0
5
4
2

INPUT DETAILS:

There are 5 cows. Cow #1 had been assigned with 1; cow #2 with 0; cow #3 with 5; cow #4 with 4; cow #5 with 2.

OUTPUT FORMAT

  • Line 1: Three space-separated integers, respectively: the maximum requested value, the position where the sequence begins, the position where the sequence ends.

SAMPLE OUTPUT (file cowxor.out)

6 4 5

OUTPUT DETAILS:

4 xor 2 = 6 (001) xor (010) = (011) 


二 分析

求出前n个数的异或结果Xor[n],那么问题转化为在n个数中找到异或结果最大的两个。因为N可能会到10w,所以O(n^2)的方法是过不了的。我们考虑建立一棵字母表为0,1的Trie树,这样给定一个数,寻找与它异或结果最大的数的时间复杂度会降到lg(Xor[i])级别(输入数字的大小不超过2^21,异或结果也不会超过2^21)。这题坑爹的地方在于usaco卡内存。。


三 代码

先附上我蠢蠢的会爆内存的trie树代码:
/*
ID:maxkibb3
LANG:C++
PROB:cowxor
*/


#include<cstdio>
#include<bitset>


const int MAX = 1e5 + 5;


int N, Arr[MAX], Xor[MAX], Trie_size;


struct Node {
	int val, left, right;


	Node() { val = 0; left = right = -1; }
	
	Node(int _v) : val(_v) { left = right = -1; }
}Trie[MAX * 5];


void create(int _v, int _f) {
	if(_v) Trie[_f].right = Trie_size;
	else Trie[_f].left = Trie_size;
	Trie[Trie_size++] = Node(_v);
}


void insert(int _v, int _i) {
	std::bitset<21> bits(_v);
	int root = 0;
	for(int i = 20; i >= 0; i--) {
		if(bits[i]) {
			if(Trie[root].right == -1)
				create(1, root);
			root = Trie[root].right;
		}
		else {
			if(Trie[root].left == -1)
				create(0, root);
			root = Trie[root].left;
		}
	}
	create(_i, root);
}


int find(int _v) {
	std::bitset<21> bits(_v);
	int root = 0;
	for(int i = 20; i >= 0; i--) {
		if(bits[i]) {
			if(Trie[root].left != -1)
				root = Trie[root].left;
			else
				root = Trie[root].right;
		}
		else {
			if(Trie[root].right != -1)
				root = Trie[root].right;
			else
				root = Trie[root].left;
		}
	}
	return Trie[Trie[root].right].val;
}


int main() {
	freopen("cowxor.in", "r", stdin);
	freopen("cowxor.out", "w", stdout);
	scanf("%d", &N);
	Trie[Trie_size++] = Node(0);
	for(int i = 1; i <= N; i++)
		scanf("%d", &Arr[i]);
	int ans = Arr[1], l = 1, r = 1;
	for(int i = 1; i <= N; i++) {
		Xor[i] = Xor[i - 1] ^ Arr[i];
		insert(Xor[i], i);
		int idx = find(Xor[i]);
		int tmp = Xor[idx] ^ Xor[i];
		if(tmp > ans) ans = tmp, l = idx + 1, r = i;
	}
	printf("%d %d %d\n", ans, l, r);
	return 0;
}



运行结果:
USER: Qi Shen [maxkibb3]
TASK: cowxor
LANG: C++

Compiling...
Compile: OK

Executing...
   Test 1: TEST OK [0.000 secs, 8664 KB]
   Test 2: TEST OK [0.000 secs, 8664 KB]
   Test 3: TEST OK [0.000 secs, 8664 KB]
   Test 4: TEST OK [0.000 secs, 8664 KB]
   Test 5: TEST OK [0.028 secs, 8928 KB]
   Test 6: TEST OK [0.084 secs, 9984 KB]
   Test 7: TEST OK [0.112 secs, 9324 KB]
   Test 8: TEST OK [0.140 secs, 11172 KB]
   Test 9: TEST OK [0.084 secs, 8664 KB]
   Test 10: TEST OK [0.056 secs, 8664 KB]
   Test 11: TEST OK [0.056 secs, 8664 KB]
   Test 12: TEST OK [0.126 secs, 10116 KB]
   Test 13: TEST OK [0.042 secs, 8664 KB]
   Test 14: TEST OK [0.098 secs, 8664 KB]
   Test 15: TEST OK [0.308 secs, 11700 KB]
   Test 16: TEST OK [0.294 secs, 11700 KB]
   Test 17: TEST OK [0.294 secs, 11700 KB]
   Test 18: TEST OK [0.210 secs, 10908 KB]
   Test 19: TEST OK [0.028 secs, 8928 KB]
   Test 20: TEST OK [0.000 secs, 8664 KB]

All tests OK.



AC代码:
/*
ID:maxkibb3
LANG:C++
PROB:cowxor
*/

#include<cstdio>
#include<bitset>
#include<cstring>
#include<map>

#define left(x) ((x << 1) + 1)
#define right(x) ((x << 1) + 2)

const int MAX = 1e5 + 5;

int N, Xor[MAX];
bool Trie[1 << 22];
std::map<int, int> M;

void insert(int _v, int _i) {
	std::bitset<21> bits(_v);
	int root = 0;
	for(int i = 20; i >= 0; i--) {
		if(bits[i]) root = right(root);
		else root = left(root);
		if(!Trie[root]) Trie[root] = 1;
	}
	std::map<int, int>::iterator it = M.find(root);
	if(it == M.end()) M.insert(std::make_pair(root, _i));
	else it->second = _i;
}

int find(int _v) {
	std::bitset<21> bits(_v);
	int root = 0, next;
	for(int i = 20; i >= 0; i--) {
		if(bits[i]) {
			next = left(root);
			if(Trie[next]) root = next;
			else root = right(root);
		}
		else {
			next = right(root);
			if(Trie[next]) root = next;
			else root = left(root);
		}
	}
	std::map<int, int>::iterator it = M.find(root);
	if(it != M.end()) return it->second;
}

int main() {
	freopen("cowxor.in", "r", stdin);
	freopen("cowxor.out", "w", stdout);
	
	scanf("%d", &N);
	for(int i = 1; i <= N; i++) {
		int tmp;
		scanf("%d", &tmp);
		Xor[i] = Xor[i - 1] ^ tmp;
	}
	
	int ans = Xor[1], l = 1, r = 1;
	for(int i = 1; i <= N; i++) {
		insert(Xor[i], i);
		int idx = find(Xor[i]);
		int tmp = Xor[idx] ^ Xor[i];
		if(tmp > ans) ans = tmp, l = idx + 1, r = i;
	}
	printf("%d %d %d\n", ans, l, r);
	return 0;
}




### 回答1: 题目描述 有N头奶牛,它们在M个牛棚之间相互转移。每个牛棚里有一些奶牛,每分钟可以容纳一头奶牛。一头奶牛从一个牛棚走到另一个牛棚需要一分钟的时间。现在,这些奶牛要开一个牛派对,它们要在同一时间到达同一个牛棚,所以它们需要在某个牛棚等待一段时间。你需要计算最小的等待时间,使得所有奶牛都能够在同一时间到达同一个牛棚。 输入格式 第一行包含三个整数N,M,X。 接下来M行,每行包含三个整数a,b,t,表示牛棚a和牛棚b之间有一条双向边,需要t分钟才能通过。 输出格式 输出一个整数,表示最小等待时间。 数据范围 1≤N≤500 1≤M≤10000 1≤X≤N 1≤a,b≤N 1≤t≤1000 输入样例#1 3 3 1 1 2 5 2 3 5 1 3 10 输出样例#1 5 输入样例#2 4 5 4 1 2 10 2 3 10 3 4 10 4 1 10 1 3 20 输出样例#2 30 算法1 (最短路) $O(N^3)$ Dijkstra算法 Dijkstra(迪杰斯特拉)算法是由荷兰计算机科学家狄克斯特拉于1956年发明的,因此又叫狄克斯特拉算法。 Dijkstra算法是一种贪心算法,用于求解一个节点到其他所有节点的最短路径。它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。 具体做法是:设立一个数组dis来保存源点到各个顶点的最短距离和一个数组book[i]来记录一个顶点是否已经在队列中。 初始时,原点s的路径权重被赋为0 (dis[s] = 0)。若对于顶点s存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,所有顶点并不属于任何已知最短路径所包含的顶点集合,因此都被标记为未知最短路径长度。当算法结束时,dis[v]中存储的便是源点s到顶点v的最短路径,或者如果从s无法到达v,则值为INF。 Dijkstra算法流程: 算法流程: 1. 将所有顶点分为两部分:已知最短路的顶点集合P和未知最短路的顶点集合Q。 2. 初始时,顶点集合P中只有源点s一个元素,以源点s为起点向外扩展。 3. 每次从顶点集合Q中选取一个顶点u(u的dist最小),并加入到顶点集合P中,同时以u为中心进行扩展。 4. 重复步骤3,直到顶点集合Q为空或者终点被加入到顶点集合P中。 5. 算法结束,最短路径保存在dis数组中。 时间复杂度 Dijkstra算法的时间复杂度为O(N^2)。由于N较小,因此可以通过本题。 参考文献 Dijkstra算法讲解 C++ 代码 算法2 (最短路) $O(N^2)$ Floyd算法 Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。 Floyd算法的基本思想 设G=(V,E)是一个带权有向图,其邻接矩阵为W。V={v1,v2,……,vn},W[1:n,1:n],则该图的Floyd算法可描述如下: 时间复杂度 Floyd算法的时间复杂度为O(N^3)。由于N较小,因此可以通过本题。 参考文献 Floyd算法讲解 C++ 代码 算法3 (最短路) $O(N^2)$ Bellman-Ford算法 Bellman-Ford算法是一种单源最短路径算法,可以处理负权边,但不能处理负权回路。 Bellman-Ford算法的基本思想 对于图中的任意一条边(u, v),Bellman-Ford算法会对每一条边进行一次松弛操作(Relax),并且这些操作是按照顺序进行的:当算法进行第i次松弛操作时,它只会改变长度为i+1的路径上的顶点的值。因此,当算法执行完第n-1次松弛操作后,路径长度最长不超过n-1,此时所有最短路径都已经求出。 时间复杂度 Bellman-Ford算法的时间复杂度为O(N*M)。由于N和M的范围较小,因此可以通过本题。 参考文献 Bellman-Ford算法讲解 C++ 代码 ### 回答2: Usaco 2007 Feb的问题是关于Cow Party的。这个问题中,农夫约翰有N头奶牛,它们之间通过一些路径相互连接,并且每个路径都有一个长度。约翰想要在某个时间将它的所有奶牛聚集在一起举办一个派对,现在他想知道所有奶牛从各自的位置到达聚会地点所需的最短时间。 为了解决这个问题,我们可以使用Dijkstra算法。我们首先需要创建一个节点集合,包含所有的奶牛和派对地点,并且初始化每个节点的最短时间为无穷大。接下来,我们选取一个起点节点--聚会地点,并将它的最短时间设置为0。然后我们开始遍历所有的节点,每次选择一个最短时间未确定的节点,并更新它的邻居节点的最短时间。我们重复这个过程,直到所有节点的最短时间都确定。 在更新节点的最短时间时,我们需要根据节点之间的路径长度来更新。我们检查从当前节点到邻居节点的路径长度加上当前节点的最短时间是否小于邻居节点目前的最短时间。如果是,则更新邻居节点的最短时间为新的最短时间。 最后,我们可以得到所有奶牛到达聚会地点所需的最短时间。我们找到所有奶牛起始位置的最长最短时间,即为我们的答案。 通过使用Dijkstra算法,我们可以解决这个问题并得到最优解。因此,Usaco 2007 Feb的Cow Party问题可以通过这种方法解决。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值