其他分享
首页 > 其他分享> > [2021.8集训Day1/JZOJ.4250]【五校联考7day1附加题】路径/洛谷P4799 [CEOI2015 Day2]世界冰球锦标赛

[2021.8集训Day1/JZOJ.4250]【五校联考7day1附加题】路径/洛谷P4799 [CEOI2015 Day2]世界冰球锦标赛

作者:互联网

目录
[2021.8集训Day1/JZOJ.4250]【五校联考7day1附加题】路径/洛谷P4799 [CEOI2015 Day2]世界冰球锦标赛

题目

思路

震惊,某OJ的评测机竟然比学校电脑室的电脑还慢!!!

这题是比较裸的折半搜索,关于折半搜索,引入一题:

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

题目

题目描述

译自 CEOI2015 Day2 T1「Ice Hockey World Championship

今年的世界冰球锦标赛在捷克举行。Bobek 已经抵达布拉格,他不是任何团队的粉丝,也没有时间观念。他只是单纯的想去看几场比赛。如果他有足够的钱,他会去看所有的比赛。不幸的是,他的财产十分有限,他决定把所有财产都用来买门票。

给出 Bobek 的预算和每场比赛的票价,试求:如果总票价不超过预算,他有多少种观赛方案。如果存在以其中一种方案观看某场比赛而另一种方案不观看,则认为这两种方案不同。

输入格式

第一行,两个正整数 \(N\) 和 \(M(1 \leq N \leq 40,1 \leq M \leq 10^{18})\),表示比赛的个数和 Bobek 那家徒四壁的财产。

第二行,\(N\) 个以空格分隔的正整数,均不超过 \(10^{16}\),代表每场比赛门票的价格。

输出格式

输出一行,表示方案的个数。由于 NNN 十分大,注意:答案\(\le 2^{40}\)。

输入输出样例

输入 #1

5 1000
100 1500 500 500 1000

输出 #1

8

说明/提示

样例解释

八种方案分别是:

  • 一场都不看,溜了溜了
  • 价格 \(100\) 的比赛
  • 第一场价格 \(500\) 的比赛
  • 第二场价格 \(500\) 的比赛
  • 价格 \(100\) 的比赛和第一场价格 \(500\) 的比赛
  • 价格 \(100\) 的比赛和第二场价格 \(500\) 的比赛
  • 两场价格 \(500\) 的比赛
  • 价格 \(1000\) 的比赛

有十组数据,每通过一组数据你可以获得 10 分。各组数据的数据范围如下表所示:

数据组号 \(1-2\) \(3-4\) \(5-7\) \(8-10\)
\(N \leq\) \(10\) \(20\) \(40\) \(40\)
\(M \leq\) \(10^6\) \(10^{18}\) \(10^6\) \(10^{18}\)

折半搜索

为了减小递归树的深度,我们把所有比赛的门票分成两部分:1到\(n/2\)​和\(n/2+1\)​到\(n\)​,各进行一次搜索,枚举门票的购买情况,一次搜索的时间就是\(O(2^{n/2}\le 2^{20})\)​.再用二分合并两部分搜索的答案,时间复杂度:\(O(2^{n/2}\cdot \log 2^{n/2})\)​,可以通过.

代码

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>

#define DEBUG 0

using std::cout;
using std::vector;
typedef long long lint ;
const int N = 50;

lint read() {
	lint re = 0;
	char c = getchar();
	bool negt = false;
	while(c < '0' || c > '9')
		negt |= (c == '-') , c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return negt ? -re : re;
}

int n;
lint m;
lint pri[N];
vector <lint> a , b;//分别存两部分搜索的结果

void dfs(int x , int end , lint curpri/*当前价格*/ , vector<lint> &p) {
	if(curpri > m)//剪枝,同时避免越界
		return;
	if(x == end + 1) {
		p.push_back(curpri);
		return;
	}
	dfs(x + 1 , end , curpri + pri[x] , p);//选第x张
	dfs(x + 1 , end , curpri , p);//不选
}
int main() {
	n = read() , m = read();
	for(int i = 1 ; i <= n ; i++)
		pri[i] = read();
		
	dfs(1 , n / 2 , 0 , a);
	dfs(n / 2 + 1 , n , 0 , b);
	//合并答案
	std::sort(b.begin() , b.end());
	lint ans = 0;
	for(int i = 0 ; i < a.size() ; i++) {
		int l = 0 , r = b.size() - 1;
		while(l < r) {
			int mid = (l + r) / 2;
			if(b[mid] <= m - a[i])
				l = mid + 1;
			else
				r = mid;
		}
		if(b[l] + a[i] > m)
			--l;
		ans += l + 1;
	}
	cout << ans << '\n';
	
#if DEBUG
	for(int i = 0 ; i < a.size() ; i++)
		cout << a[i] << ' ';
	cout << '\n';
	for(int i = 0 ; i < b.size() ; i++)
		cout << b[i] << ' ';
	cout << '\n';
#endif
	return 0;
}

回到这题,也是折半搜索,但是对复杂度的要求较高,合并的时候需要哈希,具体看代码

代码

#include <iostream>
#include <cstdio>
#include <bitset>
#include <vector>
#include <cstring>

//#pragma GCC optimized(2)
#define DEBUG 0
#define reg register
#define optimize__ 1//优化开关,关闭后用链表存储路线的hash值,比较安全(冲突概率低),但是慢(GMOJ的老爷机3s跑不出本地1.8s跑出来的数据)

//火车头
//#pragma GCC optimize(3)
//#pragma GCC target("avx")
//#pragma GCC optimize("Ofast")
//#pragma GCC optimize("inline")
//#pragma GCC optimize("-fgcse")
//#pragma GCC optimize("-fgcse-lm")
//#pragma GCC optimize("-fipa-sra")
//#pragma GCC optimize("-ftree-pre")
//#pragma GCC optimize("-ftree-vrp")
//#pragma GCC optimize("-fpeephole2")
//#pragma GCC optimize("-ffast-math")
//#pragma GCC optimize("-fsched-spec")
//#pragma GCC optimize("unroll-loops")
//#pragma GCC optimize("-falign-jumps")
//#pragma GCC optimize("-falign-loops")
//#pragma GCC optimize("-falign-labels")
//#pragma GCC optimize("-fdevirtualize")
//#pragma GCC optimize("-fcaller-saves")
//#pragma GCC optimize("-fcrossjumping")
//#pragma GCC optimize("-fthread-jumps")
//#pragma GCC optimize("-funroll-loops")
//#pragma GCC optimize("-fwhole-program")
//#pragma GCC optimize("-freorder-blocks")
//#pragma GCC optimize("-fschedule-insns")
//#pragma GCC optimize("inline-functions")
//#pragma GCC optimize("-ftree-tail-merge")
//#pragma GCC optimize("-fschedule-insns2")
//#pragma GCC optimize("-fstrict-aliasing")
//#pragma GCC optimize("-fstrict-overflow")
//#pragma GCC optimize("-falign-functions")
//#pragma GCC optimize("-fcse-skip-blocks")
//#pragma GCC optimize("-fcse-follow-jumps")
//#pragma GCC optimize("-fsched-interblock")
//#pragma GCC optimize("-fpartial-inlining")
//#pragma GCC optimize("no-stack-protector")
//#pragma GCC optimize("-freorder-functions")
//#pragma GCC optimize("-findirect-inlining")
//#pragma GCC optimize("-fhoist-adjacent-loads")
//#pragma GCC optimize("-frerun-cse-after-loop")
//#pragma GCC optimize("inline-small-functions")
//#pragma GCC optimize("-finline-small-functions")
//#pragma GCC optimize("-ftree-switch-conversion")
//#pragma GCC optimize("-foptimize-sibling-calls")
//#pragma GCC optimize("-fexpensive-optimizations")
//#pragma GCC optimize("-funsafe-loop-optimizations")
//#pragma GCC optimize("inline-functions-called-once")
//#pragma GCC optimize("-fdelete-null-pointer-checks")
//#pragma GCC optimize(2)


using std::memset;
using std::cin;
using std::cout;
using std::vector;
typedef long long lint;
typedef unsigned long long ulint;
typedef unsigned int unint;
const int N = 20;

const int mod = 1e8 + 7;
int head[mod + 10];
struct hashing {
	int id , nxt;
	ulint key;
} chain[(int)3991680 + 10];

int n , m;
int map[N][N];

struct ROUTE {
	struct bitset {
		unint a;
		inline void set() {
			a = -1;
		}
		inline void reset() {
			a = 0;
		}
		inline void change(int pos , int x) {
			if(!x)
				a &= ((-1) ^ (1u << pos));
			else
				a |= (1u << pos);
		}
		bool operator [] (const int &pos) {
			return (a >> pos) & 1;
		}
	};

	lint len;
	bitset vis;
	ROUTE() {
		len = 0;
		vis.reset();
	}
	void clear() {
		len = 0;
		vis.reset();
	}
};

typedef vector<ROUTE> vecr;

void dfs(int pre , int end , int curnum , int maxnum , ROUTE r , vecr &vec) {
	if(r.len > m)
		return;
	if(curnum == maxnum - 1) {
		r.len += map[pre][end];
		vec.push_back(r);
		return;
	}

	for(int i = 1 ; i <= n ; i++) {
		if(r.vis[i] || i == end)
			continue;
		r.vis.change(i , true);
		r.len += map[pre][i];
		dfs(i , end , curnum + 1 , maxnum , r , vec);
		r.vis.change(i , false);
		r.len -= map[pre][i];
	}
}

/*
inline int MinHash(reg ROUTE a) {
	lint res = a.len % mod;
	for(int i = 1 ; i <= n ; i++)//旧的STLbitset,比较慢
		res = (res * 3 + a.vis[i]) % mod;
	return res;
}
inline ulint ULHash(reg ROUTE a) {
	ulint res = a.len;
	for(int i = 1 ; i <= n ; i++)
		res = (res * 3 + a.vis[i]);
	return res;
}

/*/
inline int MinHash(reg ROUTE a) {
	static lint pow = 0 , l;
	if(pow == 0) {
		l = (1 << (n + 1)) - 2;
		pow = 1;
		for(int i = 0 ; i <= n ; i++)
			pow = (pow * 3ll) % mod;
	}

	return ((ulint)a.len * pow + (a.vis.a & l)) % mod;
}
inline ulint ULHash(reg ROUTE a) {
	static ulint pow = 0 , l;
	if(pow == 0) {
		l = (1 << (n + 1)) - 2;
		pow = 1;
		for(int i = 0 ; i <= n ; i++)
			pow *= 3ull;
	}
	return (ulint)a.len * pow + (a.vis.a & l);
}//*/
int main() {
	freopen("way.in" , "r" , stdin);
	freopen("way.out" , "w" , stdout);

	cin >> n >> m;
	for(int i = 1 ; i <= n ; i++)
		for(int j = 1 ; j <= n ; j++)
			cin >> map[i][j];

	vecr l , r , empt;
	empt.clear();

	reg int ans = 0;
	
	memset(head , 0 , sizeof(head));
	for(int i = 2 ; i <= n ; i++) {

		l.clear() , r.clear();
		reg ROUTE tmp;

		tmp.vis.change(1 , true);

		int mid = (n + 2) / 2;
		dfs(1 , i , 1 , mid , tmp , l);
		tmp.clear();

		tmp.vis.change(i , true);
		dfs(i , 1 , 1 , n + 2 - mid , tmp , r);

#if optimize__
#else
		memset(head , 0 , sizeof(head));
		memset(chain , 0 , sizeof(chain));
#endif

		int cnt = 0;
		int siz = 0;

		siz = r.size();
		for(reg int j = 0 ; j < siz ; ++j) {
			ulint key2 = MinHash(r[j]);
#if optimize__
			++head[key2];//可能冲突,但是快
#else
			ulint key1 = ULHash(r[j]);//极小概率冲突,但是慢
			++cnt;
			chain[cnt].key = key1 , chain[cnt].id = j , chain[cnt].nxt = head[key2] , head[key2] = cnt;
#endif
		}
		



		siz = l.size();
		for(reg int j = 0 ; j < siz ; ++j) {
			tmp.len = m - l[j].len;//tmp:与l[j]匹配的路线(不重不漏经过所有点,长度之和为m)
			tmp.vis.set();
			tmp.vis.a = tmp.vis.a ^ l[j].vis.a;

#if optimize__
			ans += head[MinHash(tmp)];
#else
			reg ulint key = ULHash(tmp);
			for(reg int k = head[MinHash(tmp)] ; k ; k = chain[k].nxt) {
				if(key == chain[k].key) {
					++ans;
				}
			}
#endif
		}
		
		
#if optimize__
		siz = r.size();//由于head比较大,这样会比memset块
		for(reg int j = 0 ; j < siz ; ++j) {
			ulint key2 = MinHash(r[j]);
			head[key2] = 0;
		}
#endif
	}
	cout << ans;
	return 0;
}

附60分做法-状压DP

\(l\)比较下,考虑\(f_{k,i,j}\)表示状态为\(k\),路径最后一个点为\(i\),路径长度为\(j\)的方案数,用dfs转移即可

另外,我们可以对\(i,j,k\)​做Hash或者map,就可以跑\(k>30\)​​的情况,用Hash极致优化一下,可以额外得到10pts的高分(最后是状态太多了存不下,时间也爆了):

//数组+map集成板
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>

typedef long long lint;
using std::pair;
using std::cout;
using std::make_pair;

int read() {
	int re = 0;
	char c = getchar();
	bool negt = false;
	while(c < '0' || c > '9')
		negt |= (c == '-') , c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return negt ? -re : re;
}


const int N = 20;
const int Status = 1 << 14;
const int L = 50;

int n , l;

int map[N][N];

namespace LargeL {
	typedef pair<pair<int , int> , int> tripair;
	tripair make(int a , int b , int c) {
		tripair tmp;
		tmp = make_pair(make_pair(a , b) , c);
		return tmp;
	}
	std::map <tripair , int> f;

	lint dfs(int k , int las , int len) {
		if(len < 0)
			return 0;

		tripair P = make(k , las , len);
		if(f.find(P) != f.end())
			return f[P];

		int sum = 0;
		for(int i = 0 ; i < n ; i++)
			if(i != las && (k >> i & 1) == 1)
				sum += dfs(k ^ (1 << las) , i , len - map[i][las]);
		return f[P] = sum;
	}
	void work() {
		f[make(1 , 0 , 0)] = 1;
		lint ans = 0;
		int final = (1 << n) - 1;
		for(int i = 1 ; i < n ; i++)
			ans += dfs(final , i , l - map[i][0]);
		cout << ans;
	}
}
namespace SmallL {
	lint f[Status][N][L];
	lint dfs(int k , int las , int len) {

		if(len < 0)
			return 0;
		if(f[k][las][len] != -1)
			return f[k][las][len];

		f[k][las][len] = 0;
		for(int i = 0 ; i < n ; i++)
			if(i != las && (k >> i & 1) == 1)
				f[k][las][len] += dfs(k ^ (1 << las) , i , len - map[i][las]);

		return f[k][las][len];
	}
	void work() {
		memset(f , -1 , sizeof(f));
		f[1][0][0] = 1;
		lint ans = 0;
		int final = (1 << n) - 1;
		for(int i = 1 ; i < n ; i++)
			ans += dfs(final , i , l - map[i][0]);
		cout << ans;
	}
}

int main() {
//	std::freopen("way.in" , "r" , stdin);
//	std::freopen("way.out" , "w" , stdout);
	
	n = read() , l = read();
	for(int i = 0 ; i < n ; i++)
		for(int j = 0 ; j < n ; j++)
			map[i][j] = read();

	if(l <= 30)
		SmallL::work();
	else
		LargeL::work();
	return 0;
}
//Hash板
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>


typedef long long lint;
using std::cout;
using std::make_pair;
using std::pair;
const int N = 20;
const int Status = 1 << 14;
const int L = 50;

int read() {
	int re = 0;
	char c = getchar();
	bool negt = false;
	while(c < '0' || c > '9')
		negt |= (c == '-') , c = getchar();
	while(c >= '0' && c <= '9')
		re = (re << 1) + (re << 3) + c - '0' , c = getchar();
	return negt ? -re : re;
}
class Hashing {
	private :
		typedef unsigned long long ulint;
		static const ulint mod = 10000009;
		struct node {
			ulint key;
			int dat;
			int nxt;
		}chain[N * L * Status];
		inline ulint hash1(ulint a , ulint b , ulint c) {
			return a * (N + 1) * (L + 1) + b * (L + 1) + c;
		}
		inline ulint hash2(ulint a , ulint b , ulint c) {
			return (a * (N + 1) % mod * (L + 1) % mod + b * (L + 1) % mod + c) % mod;
		}
		int head[mod + 10];
		int cnt;
	public :
		int find(ulint a , ulint b , ulint c) {
			int key = hash1(a , b , c);
			for(int i = head[hash2(a , b , c)] ; i ; i = chain[i].nxt) {
				if(key == chain[i].key)
					return chain[i].dat;
			}
			return -1;
		}
		inline void insert(ulint a , ulint b , ulint c , int dat) {
			if(find(a , b , c) != -1)
				return;
			ulint k1 = hash1(a , b , c) , k2 = hash2(a , b , c);
			++cnt;
			chain[cnt].key = k1 , chain[cnt].dat = dat , chain[cnt].nxt = head[k2] , head[k2] = cnt;
			
		}
};


int n , l;

int map[N][N];


typedef pair<pair<int , int> , int> tripair;
tripair make(int a , int b , int c) {
	tripair tmp;
	tmp = make_pair(make_pair(a , b) , c);
	return tmp;
}

Hashing f;

lint dfs(int k , int las , int len) {
	if(len < 0)
		return 0;
	
	int tmp = f.find(k , las , len);
	if(tmp != -1)
		return tmp;
	
	int sum = 0;
	for(int i = 0 ; i < n ; i++)
		if(i != las && (k >> i & 1) == 1)
			sum += dfs(k ^ (1 << las) , i , len - map[i][las]);
	f.insert(k , las , len , sum);
	return sum;
}
int main() {
	std::freopen("data//way8.in" , "r" , stdin);
	std::freopen("way.out" , "w" , stdout);
	n = read() , l = read();
	for(int i = 0 ; i < n ; i++)
		for(int j = 0 ; j < n ; j++)
			map[i][j] = read();
	
	
	f.insert(1 , 0 , 0 , 1);
	
	lint ans = 0;
	int final = (1 << n) - 1;
	for(int i = 1 ; i < n ; i++)
		ans += dfs(final , i , l - map[i][0]);
	cout << ans;
	return 0;
}

标签:std,GCC,洛谷,int,2021.8,optimize,pragma,include,联考
来源: https://www.cnblogs.com/dream1024/p/15126603.html