您的位置:首页 > 其它

状态压缩——hihoCoder 1087

2017-02-14 16:06 197 查看
题目链接: https://hihocoder.com/problemset/problem/1087

题意: 给出一个N个点,M条边的有向图,求其中有多少条哈密顿回路

分析:这道题非常直接的做法就是DFS搜索了,我们可以从任一顶点出发,不走重复点,若能回到顶点,那么哈密顿回路数量+1。但是直接暴力搜会超时,所以需要加上一个位运算的优化。

位运算搜索:

将邻接链表压缩成一个二进制串,第 i 位为1 则表示这个点能到达点 i,为 0 则表示不能到达。

用一个二进制串表示状态,第 i 位为1则标识未访问过

搜索代码:

int n,m;
int loc[1<<13]; //存储只含有一个1的二进制串的1在第几位上
int edge[MaxN];
int ans = 0;
void dfs(int u, int stat)
{
if( !stat ) ans += (edge[u]&1);
else
{
int rest = stat & edge[u];
while(rest)
{
int v = rest&(-rest);
dfs(loc[v], stat-v);
rest -= v;
}
}
}
void solve()
{
dfs(1, (1<<n)-2);
print(ans);
cout << endl;
}

int main()
{
scan(n);scan(m);
for(int i=0;i<n;i++) loc[1<<i] = i+1;
while(m--)
{
int a,b;
scan(a);scan(b);
edge[a] |= 1 << (b-1);
}
solve();
system("pause");
}


在搜索中我们发现,1-2-3-4 和 1-3-2-4 对应的都是 dfs(4, stat)这个stat也都是一样的,像这种类似的情况都是做了重复的计算,所以我们可以将它们记录下来,变成记忆化搜索:

状态:DP[i][stat]表示搜索到第 i 个节点,且 N个节点的访问状况为一个二进制串 stat

转移方程: 如果上一个节点 j 可以到达当前节点 i 且当前状态 stat 表示已经访问过第 i 个节点,且 i!=j 那么:

if( j!=i && (edge[j]&(1<<(i-1))) &&  (stat&(1<<(i-1)))==0 )
DP[i][stat] += DP[j][stat+(1<<(i-1))];


for循环遍历顺序,由于我们是从stat 有N-1个1(即访问过1节点,假定1节点为起点),那么我们需要从按含有1的个数从N-1到0来搜索stat,对于每一个stat,我们再两层for循环遍历上一个节点和当前节点的组成情况(此处两个for循环的顺序可调换)

DP代码:

int n,m;
int nums[1<<MaxN];
int edge[MaxN];
int DP[MaxN][(1<<MaxN)];
void init()
{
for(int i=0;i<(1<<n);i++)
{
int tmp = 0;
int x = i;
while(x)
{
tmp ++;
x&=(x-1);
}
nums[i] = tmp;
}
}

void solve()
{
init();
DP[1][(1<<n)-2] = 1;
for(int t=n-1;t>=0;t--)
{
for(int stat=0;stat<(1<<n);stat++)
{
if(nums[stat] == t)
{
//cout << bitset<8>(stat) << endl;
for(int j=1;j<=n;j++)
{
for(int i=1;i<=n;i++)
{
if( j!=i && (edge[j]&(1<<(i-1))) && (stat&(1<<(i-1)))==0 ) DP[i][stat] += DP[j][stat+(1<<(i-1))];
}
}
}
}
}
int ans = 0;
for(int i=1;i<=n;i++)
{
if(edge[i]&1)ans += DP[i][0];
}
print(ans);
cout << endl;
}

int main()
{
scan(n);scan(m);
while(m--)
{
int a,b;
scan(a);scan(b);
edge[a] |= 1 << (b-1);
}
solve();
system("pause");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: