路径(折半搜索)(状态压缩)(哈希)
路径
题目大意
有 n 个城市,任意两个之间有一定长度的路径。
然后让你从 1 号点出发不重不漏的走过每个点然后最后回到 1 号点(也就是 1 号点经过两次,其它点经过一个)。
然后问你要多少种方案是走的路径长度和恰好是 l。
思路
看着这个范围,看着这个题,不难想到可以状压 DP。
然而发现它要路径长度和恰好是 l,你要表示的话就要再加一维表示当前的路径长度,然后尽管你开 map 啊哈希啊这些搞它还是会超时。
然后你看着这个
14
14
14,要想到一个叫做折半搜索的东西。
我们可以枚举一个
i
i
i,然后把路径分成
1
→
.
.
.
→
i
→
.
.
.
→
1
1\rightarrow...\rightarrow i\rightarrow...\rightarrow 1
1→...→i→...→1 两段。
然后由于
i
→
j
i\rightarrow j
i→j 的费用跟
j
→
i
j\rightarrow i
j→i 的一样,那就相当于两条
1
→
.
.
.
→
i
1\rightarrow...\rightarrow i
1→...→i 的路径满足长度相加是
l
l
l 且除了
1
,
i
1,i
1,i 没有共有的点。
然后不按你想到暴搜出每一种可能(反正只有
7
7
7 步,折了半,
n
!
n!
n! 都没问题),然后走的点就状压一下,然后至于权值就用哈希表。
然后就一边暴搜出来放进哈希表,一遍暴搜出来去哈希表配对。
然后就好了。
代码
#include<cstdio>
#include<cstring>
#define mo 997
using namespace std;
struct node {
int x, to, nxt;
}e[1000001];
int n, l, a[15][15], nn, mm, zt;
int KK, hash[8192][997], now, ans;
bool in[15];
void clear_hash() {
memset(hash, 0, sizeof(hash));
KK = 0;
}
void hash_insert(int pl, int val) {
int pla = val % mo;
for (int i = hash[pl][pla]; i; i = e[i].nxt)
if (e[i].to == val) {
e[i].x++;
return ;
}
e[++KK] = (node){1, val, hash[pl][pla]}; hash[pl][pla] = KK;
}
int hash_query(int pl, int val) {
val = l - val;//得到你对于的另一个半的值
pl = ((1 << (n - 1)) - 1) ^ pl;
pl ^= (1 << (now - 1));
int pla = val % mo;
for (int i = hash[pl][pla]; i; i = e[i].nxt)
if (e[i].to == val) {
return e[i].x;
}
return 0;
}
void dfs(int sum, int lst, int num) {//暴搜左边一半放进哈希里面
if (num == nn) {
if (sum + a[lst][now] > l) return ;
hash_insert(zt >> 1, sum + a[lst][now]);
return ;
}
if (sum > l) return ;
for (int i = 1; i < n; i++)
if (!in[i]) {
in[i] = 1;
zt ^= (1 << i);
dfs(sum + a[lst][i], i, num + 1);
zt ^= (1 << i);
in[i] = 0;
}
}
void dfs1(int sum, int lst, int num) {//暴搜右边一半然后去哈希里面匹配
if (num == mm) {
if (sum + a[lst][now] > l) return ;
ans += hash_query(zt >> 1, sum + a[lst][now]);
return ;
}
if (sum > l) return ;
for (int i = 1; i < n; i++)
if (!in[i]) {
in[i] = 1;
zt ^= (1 << i);
dfs1(sum + a[lst][i], i, num + 1);
zt ^= (1 << i);
in[i] = 0;
}
}
int main() {
// freopen("way.in", "r", stdin);
// freopen("way.out", "w", stdout);
scanf("%d %d", &n, &l);
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
scanf("%d", &a[i][j]);
nn = (n - 2) / 2;
mm = (n - 2) - nn;
for (now = 1; now < n; now++) {//枚举最中间是哪个点
clear_hash();
in[now] = 1;
dfs(0, 0, 0);
dfs1(0, 0, 0);
in[now] = 0;
}
printf("%d", ans);
fclose(stdin);
fclose(stdout);
return 0;
}
相关文章
- hdu4771 水搜索(状态压缩+bfs)
- JAVAEE之-----MySQL分页技术(带搜索)
- 20Linux - 文件管理(文本搜索:grep)
- 1115 Counting Nodes in a BST (30 分)【难度: 一般 / 知识点: 构建二叉搜索树】
- Vue - Ant Design Vue表格组件(Table)分页器(Pagination)带搜索条件调用接口进行分页查询
- 微信小程序搜索框代码组件
- el-tree可搜索单选
- 使用 SwiftUI 构建可搜索列表,为您的 iOS 应用程序创建具有自动完成功能的可搜索列表(教程含源码)
- Flutter 教程之 在 Flutter 中生成 JSON 模型,在 Flutter GetX 中过滤列表和延迟搜索
- macOS SwiftUI 开发教程之经典双栏搜索框界面 (教程含源码)
- 剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
- 判断一棵二叉树是否为二叉搜索树
- 查看并添加python中库的搜索路径
- 【华为机试真题】专栏文章搜索说明
- 【付费推广】常见问题合集,搜索推广FAQ 3
- 【Python爬虫】:爬取(谷歌/百度/搜狗)的搜索结果
- [LeetCode] Binary Search Tree Iterator 二叉搜索树迭代器
- 【bzoj5123】[Lydsy12月赛]线段树的匹配 树形dp+记忆化搜索