JZOJ 4250. 【五校联考7day1附加题】路径(折半搜索)

JZOJ 4250. 【五校联考7day1附加题】路径

题目

Description

A国有 n n n个城市,编号为 1 1 1 n n n,任意两个城市之间有一条路。shlw闲得没事干想周游A国,及从城市 1 1 1出发,经过且仅经过除城市 1 1 1外的每个城市 1 1 1次(城市 1 1 1两次),最后回到城市 1 1 1。由于shlw很傻,他只愿意走一定长度,多了少了都不干,现在他想知道一共有多少种方案可供选择。

Input

第一行为两个整数 n , l n,l nl,分别为城市数和路程总长。
之后 n n n行,每行 n n n个整数,其中第 i i i行第 j j j列为 A i , j A_{i,j} Ai,j,满足 A i , i = 0 , A i , j = A j , i A_{i,i}=0,A_{i,j}=A_{j,i} Ai,i=0,Ai,j=Aj,i.

Output

输出共一行,为总方案数。

Sample Input

3 6
0 1 3
1 0 2
3 2 0

Sample Output

2

Data Constraint

对于30%, 1 ≤ n ≤ 10 1≤n≤10 1n10.
对于另外30%, 1 ≤ n ≤ 14 , 1 ≤ l ≤ 30 1≤n≤14,1≤l≤30 1n141l30.
对于100%, 1 ≤ n ≤ 14 , 1 ≤ A i , j ≤ 1 0 8 , 1 ≤ l ≤ 2 ∗ 1 0 9 1≤n≤14,1≤A_{i,j}≤10^8,1≤l≤2*10^9 1n141Ai,j1081l2109.
悄悄告诉你:数据的答案都很大,反正直接输 0 0 0是没分的,但都在int范围内。
为了降低题目难度, A i , j A_{i,j} Ai,j的种类数不会太多

题解

  • 看到数据限制的最后一行我什么都不想说。。。

  • 这句话能说明什么我现在都不清楚。。。

  • 进入正题,

  • 第一档的部分分可以暴力DFS解决,

  • 第二档的部分分可以状压DP解决,

  • 第三档呢?

  • 也能像第二档一样状压DP吗?

  • 乍一看好像是可以的,怎么记录路径长度和呢?

  • GG了,此方法不可做——

  • DFS吗?直接这么做似乎最大时间复杂度 O ( n ! ) = 14 ! = 87178291200 ≈ 8.7 ∗ 1 0 10 O(n!)=14!=87178291200≈8.7*10^{10} O(n!)=14!=871782912008.71010,显然超时,

  • 有一种神奇的搜索叫做折半搜索!!!

  • 顾名思义,把要搜索的东西给折半

  • 主要过程如下:

  • 前一半,DFS找出从 1 1 1出发经过的一半的点得到的状态,记录下它和对应的方案数。

  • 状态包括到达的点 k k k,经过了点的状态 s s s(状态压缩,不包括 1 1 1),路径长度和 s u m sum sum.

  • 这些用哈希表存下来,对于相同的 k , s , s u m k,s,sum k,s,sum,不需重复记录,只要给对应的方案数 + 1 +1 +1.

  • 后一半,同样使用DFS找出从 1 1 1出发经过一半的点得到的状态,不需记录,

  • 状态包括到达的点 k k k,经过了点的状态 s s s(状态压缩,不包括 1 1 1),路径长度和 s u m sum sum.

  • 此时,到哈希表里找到和这个状态对应的(能组成一条完整的路线的)状态,

  • 直接查找 ( k , s , s u m ) (k,s,sum) (k,s,sum)

  • 当然不是!(难道两条完全相同的路径能组成一条符合题目条件的回路吗?)

  • 第一维,查找的是 k k k,对应走到的当然要是相同的点。

  • 第二维,查找的是 2 n − 1 − s − 1 + 2 k − 1 2^n-1-s-1+2^{k-1} 2n1s1+2k1 2 n − 1 2^n-1 2n1是包括每个点的压缩后的状态, − s -s s是减去了当前经过的点压缩后的状态, − 1 -1 1是减去点 1 1 1的状态, + 2 k − 1 +2^{k-1} +2k1是加回当前点的状态。

  • 第三维,查找的是 l − s u m l-sum lsum,因为两半的路径和要等于 l l l

  • 所以后一半到哈希表里查找 ( k , 2 n − 1 − s − 1 + 2 k − 1 , l − s u m ) (k,2^n-1-s-1+2^{k-1},l-sum) (k,2n1s1+2k1,lsum),找到则加上对应的方案数

代码

#include<cstdio>
#include<cstring>
using namespace std;
#define md 9999997
#define LL long long
int n,len;
int dis[15][15],e[15],bz[15];
LL ans=0;
struct
{
	int k,s,sum;
	LL tot;
}hs[20000010];
void hash(LL k,LL s,LL sum)
{
	LL x=k*s%md*sum%md;
	int ok=0;
	while(hs[x].k)
	{
		if(hs[x].k==k&&hs[x].s==s&&hs[x].sum==sum) 
		{
			hs[x].tot++;
			ok=1;
			break;
		}
		x++;
		if(x>20000000) x=0;
	}
	if(!ok) hs[x].k=k,hs[x].s=s,hs[x].sum=sum,hs[x].tot=1;
}
void find(LL k,LL s,LL sum)
{
	LL x=k*s%md*sum%md;
	while(hs[x].k)
	{
		if(hs[x].k==k&&hs[x].s==s&&hs[x].sum==sum)
		{
			ans+=hs[x].tot;
			break;
		}
		x++;
		if(x>20000000) x=0;
	}
}
int ss=0;
void dfs(int k,int x,int s,int sum,int z)
{
	if(x>z) hash(k,s,sum),ss++;
	else
	{
		for(int i=2;i<=n;i++) if(!bz[i])
		{
			bz[i]=1;
			if(sum+dis[k][i]<=len) dfs(i,x+1,s+e[i-1],sum+dis[k][i],z);
			bz[i]=0;
		}
	}	
}
void dfs1(int k,int x,int s,int sum,int z)
{
	if(x>z) find(k,e[n]-s-2+e[k-1],len-sum);
	else
	{
		for(int i=2;i<=n;i++) if(!bz[i])
		{
			bz[i]=1;
			if(sum+dis[k][i]<=len) dfs1(i,x+1,s+e[i-1],sum+dis[k][i],z);
			bz[i]=0;
		}
	}	
}
int main()
{
	int i,j;
	scanf("%d%d",&n,&len);
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++) scanf("%d",&dis[i][j]);
	e[0]=1;
	for(i=1;i<=14;i++) e[i]=e[i-1]*2;
	if(n%2==0) 
	{
		dfs(1,1,0,0,n/2);
		dfs1(1,1,0,0,n/2);
	}
	else
	{
		dfs(1,1,0,0,n/2);
		dfs1(1,1,0,0,n/2+1);
	}
	printf("%lld",ans);
	return 0;
}
哈哈哈哈哈哈哈哈哈哈
原文地址:https://www.cnblogs.com/LZA119/p/13910089.html