Description
A国有n个城市,编号为1到n,任意两个城市之间有一条路。shlw闲得没事干想周游A国,及从城市1出发,经过且仅经过除城市1外的每个城市1次(城市1两次),最后回到城市1。由于shlw很傻,他只愿意走一定长度,多了少了都不干,现在他想知道一共有多少种方案可供选择。
Input
第一行为两个整数n,l,分别为城市数和路程总长。
之后n行,每行n个整数,其中第i行第j列为Ai,j,满足Ai,i=0;Ai,j=Aj,i。
Output
输出共1行,为总方案数。
Sample Input
3 6
0 1 3
1 0 2
3 2 0
Sample Output
2
Data Constraint
对于30%,1<=n<=10
对于另外30%,1<=n<=14,1<=l<=30
对于100%,1<=n<=14,1<=Ai,j<=100,000,000,1<=l<=2,000,000,000
悄悄告诉你:数据的答案都很大,反正直接输0是没分的,但都在int范围内。
为了降低题目难度,Ai,j的种类数不会太多
思路
对于一条哈密尔顿回路,除了1外另找一个点i则该路径为1-……-i-……-1。记前半段除了1与i长度为n1,后半段为n2,那么如果确定了i,只要满足前半段与后半段经过的点不重复(除1)且路径总长为l。具体来说就是就是枚举一个点i,用P(n-2,n1)的时间枚举前半段的所有情况,用hash记下来(拉链,c++用map估计会T),再用P(n-2,n2)的时间枚举后半段的所有情况,并在hash中找到与之对应的前半段,统计进答案中。这样的话复杂度为(n-1)(P(n-2,n1)+P(n-2,n2))(hash均摊复杂度),显然n1取trunc(n/2)最优。
代码
#include<cstdio>
#include<iostream>
#include<cstring>
#define mod 19260817
//明天我就要被枪毙了(斜眼笑)
#define ll long long
#define N 15
using namespace std;
int n,m,l,a[N][N],f[mod],ans,i;
ll g[mod];
ll calc(int x,int y,int k)
{
return x*32768000000000ll+y*2000000000ll+k;
}
int gethash(int x,int y,int k)
{
ll r=calc(x,y,k);
for(i=r%mod; g[i]&&g[i]!=r; ++i==mod?i=0:i);
return i;
}
void dfs1(int d,int x,int y,int k)
{
if(x&&k>=l) return;
if(d>=m)
{
int p=gethash(x,y,k);
g[p]=calc(x,y,k),f[p]++; return;
}
for(int i=1; i<n; i++) if(!(y>>i&1)) dfs1(d+1,i,y|1<<i,k+a[x][i]);
}
void dfs2(int d,int x,int y,int k)
{
if(x&&k>=l) return;
if(d>=m)
{
int p=gethash(x,((~y)&((1<<n)-1))|1|1<<x,l-k);
ans+=f[p]; return;
}
for(int i=1; i<n; i++) if(!(y>>i&1)) dfs2(d+1,i,y|1<<i,k+a[x][i]);
}
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]);
m=n/2,dfs1(0,0,1,0),m=n-m,dfs2(0,0,1,0),printf("%d",ans);
}