【汕头市选2014】舞伴(perm)

本文介绍了一个利用状态压缩动态规划(状压DP)解决特定组合问题的方法:即如何从N个男孩和N个女孩中找到尽可能多的不同配对方案,使每一对都是朋友关系,并给出了一种避免超时的优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

Description

N 个男孩,N 个女孩,男孩和女孩可能是朋友,也可能不是朋友。现在要组成N 对舞伴,要求每对舞

伴都是一男一女,且他们是朋友。

统计不同配对方案的数量,因为结果很大,所以只要求除以M 的余数。

Input

第1 行,2 个整数N,M。接下来N 行,每行N 个整数Aij,表示第i 个男孩和第j 个女孩的关系。如果他们是朋友,则Aij = 1,否则Aij = 0。

Output

1 个整数,表示所求的值。

Sample Input

3 1000000000

1 1 1

1 1 1

1 1 1

Sample Output

6

Data Constraint

• 对于50% 的数据,N <= 9;

• 对于100% 的数据,1 <= N <= 20, 1 <= M <= 10^9; 0 <= Aij <= 1。

题解

一眼状压,但是发现普通状压是2nn2的会超时
然后发现如果一个二进制可能可以被转移,那么一定有i个位是1
所以先把所有二进制按照1的个数排序再做就不会tle了

贴代码

var
    i,j,k,n,t:longint;
    f:array[0..21,0..1100000]of int64;
    war:array[0..1100000,1..2]of longint;
    a:array[0..25,0..25]of longint;
    op,ed:array[0..25]of longint;
    x,y,z,m:int64;
procedure qsort(l,r:longint);
var
    i,j,mid:longint;
begin
    i:=l;
    j:=r;
    mid:=war[(i+j) div 2,2];
    repeat
        while war[i,2]<mid do inc(i);
        while war[j,2]>mid do dec(j);
        if i<=j then
        begin
            war[0]:=war[i];
            war[i]:=war[j];
            war[j]:=war[0];
            inc(i);
            dec(j);
        end;
    until i>j;
    if i<r then qsort(i,r);
    if l<j then qsort(l,j);
end;
begin
    //assign(input,'t1.in'); reset(input);
    assign(input,'perm.in'); reset(input);
    assign(output,'perm.out'); rewrite(output);
    readln(n,m);
    for i:=1 to n do
    begin
        for j:=1 to n do
        begin
            read(k);
            if k=0 then continue;
            inc(a[i,0]);
            a[i,a[i,0]]:=j;
        end;
        readln;
    end;
    for i:=1 to 1<<n-1 do
    begin
        war[i,1]:=i;
        k:=0;
        for j:=1 to n do
            if 1<<(j-1) and i<>0 then inc(k);
        war[i,2]:=k;
    end;
    qsort(1,1<<n-1);
    op[0]:=0;
    ed[0]:=0;
    op[1]:=1;
    for i:=2 to 1<<n-1 do
    if war[i,2]<>war[i-1,2] then
    begin
        ed[war[i-1,2]]:=i-1;
        op[war[i,2]]:=i;
    end;
    ed[war[i,2]]:=i;
    f[0,0]:=1;
    war[0,1]:=0;
    for i:=1 to n do
        for t:=op[i-1] to ed[i-1] do
        begin
            j:=war[t,1];
            for k:=1 to a[i,0] do
            begin
                x:=(1<<(a[i,k]-1)) and j;
                y:=(1<<(a[i,k]-1)) xor j;
                if x and j=0 then
                f[i,y]:=(f[i,y]+f[i-1,j]) mod m;
            end;
        end;
    writeln(f[n,1<<n-1]);
    close(input); close(output); 
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值