Codeforces Beta Round #11, problem: (D) A Simple Task 状态压缩DP+记忆化搜素DP

题意:在图中找简单回路

/*************
看了大神的博客才有所感悟啊,记忆化搜索+状态压缩。。。。太神了...
这种复杂度,近百万的DFS复杂度居然没有TLE,果然经验不足,菜鸟一只。
用状态压缩枚举起点和可能经过的点。
可以判定的简单通路 i->j,存在的条数为sum(i->k) 其中k,j之间有边。
当然,每次计算通路个数的时候,可以借每一个k来判断回路的条数,当然只能算一次。
这样加出来的回路会有重复,因为可能把顺逆两种方向运动的回路都考虑进去。
记忆化搜索的好处是可以精确的计算每次遍历点的情况,而如果单纯只是循环的话,却没有这么
灵活。
****************/
#define LL long long
#include<cstdio>
#include<cstring>
const int LMT=22,LMS=1<<19;
LL dp[LMS][LMT],ans;
int n,start,tem,gra[LMT][LMT];
int get_one(int x)
{
    int res=0;
    do
    res+=x&1;
    while(x>>=1);
    return res;
}
int left(int x)
{
    int d=x&(-x),res=0;
    while(d>>=1)res++;
    return res;
}
LL dfs(int mas,int end)
{
    if(dp[mas][end]>=0)return dp[mas][end];
    LL res=0;
    for(int j=start;j<n;j++)
    if(gra[j][end]&&((1<<j)&mas)&&(tem==2||j!=start))
    {
        tem--;
        res+=dfs(mas^(1<<end),j);
        tem++;
    }
    if(tem>2&&gra[end][start])
        ans+=res;
    dp[mas][end]=res;
    return res;
}
int main(void)
{
    int m,lim;
    scanf("%d%d",&n,&m);
    memset(dp,-1,sizeof(dp));
    while(m--)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        u--;v--;
        gra[u][v]=gra[v][u]=1;
    }
    lim=1<<n;
    for(int i=0;i<n;i++)dp[1<<i][i]=1;
    for(int t=0;t<lim;t++)
    {
        start=left(t);
        tem=get_one(t);
        for(int j=start;j<n&&tem>1;j++)
        if(gra[j][start]&&((1<<j)&t))
           dfs(t,j);
    }
    printf("%I64d\n",ans>>1);
    return 0;
}


你可能感兴趣的:(Codeforces Beta Round #11, problem: (D) A Simple Task 状态压缩DP+记忆化搜素DP)