• P1896 [SCOI2005]互不侵犯


      我好久都没有发博客了……

      可不是因为我没做题啊,因为懒得发qwq……

      今天发一波状压dp(就是dp)。

      我还想不到如果不是格子图,状压dp怎么用……

      因为它与dp的不同就是它可以用二进制来表示这一位(这个格子选不选)……

      dp [ i ] [ j ] [ t ] 表示第 i 行,第 j 个状态,选择 t 个的方案数……

      其实因为每一行都一样,所以其实所谓状态 j 就是第 j 种放法罢了,而第几行其实无所谓,因为每一行都一样。

      当然一开始第1行,第 j 种状态,放第 j 种状态下应该放的数量的方案数自然是1。

      接下来上代码:

    #include<cstdio>
    #include<iostream>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define int long long
    int dp[20][200][200],mark[200],uni[200],ans[200],sta[200];
    vector<int> sec[900];
    int n,k,tot;
    void dfs(int x)
    {
        if(x==n+1)
        {
            tot++;
            for(int i=1; i<=n; i++)
            {
                sta[tot]*=2;
                sta[tot]+=ans[i];
                mark[tot]+=ans[i];
            }
            return ;
        }
        ans[x]=0;
        dfs(x+1);
        if(!ans[x-1])
        {
            ans[x]=1;
            dfs(x+1);
            ans[x]=0;
        }
    }
    bool ac(int l1,int l2)
    {
        for (int i = 2; i < n; i++)
        {
            if ((sta[l1] & uni[i]) && ((sta[l2] & uni[i - 1]) || (sta[l2] & uni[i]) || (sta[l2] & uni[i + 1])))
                return 0;
            if ((sta[l2] & uni[i]) && ((sta[l1] & uni[i - 1]) || (sta[l1] & uni[i]) || (sta[l1] & uni[i + 1])))
                return 0;
        }
        if( (sta[l1] & uni[1]) && ( (sta[l2] & uni[1]) || sta[l2] & uni[2] ) ) return 0;
        if( (sta[l1] & uni[n]) && ( (sta[l2] & uni[n]) || sta[l2] & uni[n-1]) ) return 0;
        if( (sta[l2] & uni[1]) && ( (sta[l1] & uni[1]) || sta[l1] & uni[2] ) ) return 0;
        if( (sta[l2] & uni[n]) && ( (sta[l1] & uni[n]) || sta[l1] & uni[n-1]) ) return 0;
        return 1;
    }
    main()
    {
        scanf("%lld%lld",&n,&k);
        if(n==0)
        {
            printf("0");
            return 0;
        }
        if(n==1)
        {
            if(k==0) printf("1");
            else printf("0");
            return 0;
        }
        uni[n]=1;
        for(int i=n-1; i>=1; i--)
            uni[i]=uni[i+1]<<1;
        dfs(1);
        for(int i=1; i<=tot; i++)
            for(int j=1; j<=tot; j++)
                if(ac(i,j)) sec[i].push_back(j);
        for(int i=0; i<=tot; i++)
            dp[1][i][mark[i]]=1;
        for(int i=1; i<=n; i++)
            for(int j=1; j<=tot; j++)
                for(int t=mark[j]; t<=k; t++)
                    for(int o=0; o<sec[j].size(); o++)
                    {
                        dp[i][j][t]+=dp[i-1][sec[j][o]][t-mark[j]];
                    }
        int sum=0;
        for(int i=1; i<=tot; i++)
            sum+=dp[n][i][k];
        printf("%lld",sum);
        return 0;
    
    }
  • 相关阅读:
    (剑指offer)斐波那契数列
    手写Vue源码 watch的实现
    Vue源码之异步批量任务更新
    手写Vue源码之 依赖收集
    C# 测试代码#if DEBUG使用
    shell脚本编程相关7
    C#中关于ref和out的认识
    shell脚本编程相关6
    shell脚本编程相关5
    shell脚本编程相关4
  • 原文地址:https://www.cnblogs.com/popo-black-cat/p/10324894.html
Copyright © 2020-2023  润新知