[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