jzoj 5230_队伍统计_dp

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jzoj 5230_队伍统计_dp相关的知识,希望对你有一定的参考价值。

题目描述

现在有n个人要排成一列,编号为1->n 。但由于一些不明原因的关系,人与人之间可能存在一些矛盾关系,具体有m条矛盾关系(u,v),表示编号为u的人想要排在编号为v的人前面。要使得队伍和谐,最多不能违背k条矛盾关系(即不能有超过k条矛盾关系(u,v),满足最后v排在了u前面)。问有多少合法的排列。答案对10^9+7取模。


 

思路

看到数据范围就应该可以想到可以用状压dp,我们用f[i][j]表示第i终状态违背了j次的排列数量,i这个状态在二进制中表示每一位有没有被选到

我们先考虑转移,我们如果要将一个人加入到一种状态里面,显然当他加进去时总的违背数量不可以超过k,我们可以处理出一个数组a[i],表示和i冲突的人的具体状态(二进制)

对于i这个状态,如果我们要加入一个人x,那么(i&a[x])中1的个数+j一定要<=k

然后我们再可以预处理一个数组tot[i],表示i这个状态里面一共有多少个1,其他的就没什么好讲的了


#include <stdio.h>
#include <iostream>
#define LL long long
#define p 1000000007
#define maxn 1 << 22
inline int read()
{
    int x=0;char ch=getchar();
    while (ch<0||ch>9){ch=getchar();}
    while (ch>=0&&ch<=9){x=(x<<1)+(x<<3)+ch-0;ch=getchar();}
    return x;
}
int tot[maxn], a[22], f[maxn][22];
int main()
{
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    int n = read(), m = read(), k = read();
    for (int i = 0; i <= (1 << n) - 1; i++)
    {
        int x = i;
        while (x)
        {
            tot[i]++;
            x = x & (x - 1);
        }
    }
    for (int i = 1; i <= m; i++)
    {
        int x = read(), y = read();
        a[x] |= 1 << (y - 1);
    }
    f[0][0] = 1;
    for (int i = 0; i <= (1 << n) - 1; i++)
        for (int j = 0; j <= k; j++)
            if (f[i][j])
            {
                for (int x = 1; x <= n; x++)
                {
                    if (~i&(1<<(x-1)))
                        if (j + tot[i&a[x]] <= k)
                            f[i|1<<(x-1)][j + tot[i&a[x]]] = (f[i|1<<(x-1)][j + tot[i&a[x]]] + f[i][j]) % p;
                }
            }
    long long ans = 0;
    for (int i = 0; i <= k; i++)
        ans += f[(1<<n)-1][i]%p;
    std::cout << ans % p << std::endl;
}

 

以上是关于jzoj 5230_队伍统计_dp的主要内容,如果未能解决你的问题,请参考以下文章

HDU_5230_DP

从jzoj6830/loj3395受到的启发

[JZOJ5398]:Adore(状压DP+记忆化搜索)

JZOJ_7.15C组第四题 城市统计

动态规划_计数类dp_数位统计dp_状态压缩dp_树形dp_记忆化搜索

jzoj5990. 北大2019冬令营模拟2019.1.6Bear (状压dp)