【JZOJ A组】路径

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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值