ZOJ 3738 Buy the Pets 状压DP

在虚拟OJ上挂了一场ZOJ的月赛,话说这场还真有点难。

看到B题的时候,以为是图论,想了半个小时,觉得搞不动,就放了,于是去做D了,D做出来了,突然发现F题是某年区域赛的变形(似乎是成都赛区),于是又去搞F了(结果没搞出来  o(>﹏<)o  )

尼玛,从来就没有看出来B是状压啊!!!我还是太弱了。。。


好了,说这题怎么解吧。网上有两种解法。

第一种:画一个矩阵方格,y坐标为人,x坐标为猫和狗。这就变成了经典的国际象棋放车问题。如果有矛盾关系,该方格不能放车,根据题意,每排有且仅放2个车(每个人都要配一只猫一只狗)。放车的方案数,就是配对的方案数。

第二种:因为人和狗没有矛盾关系。还是放车问题。但是分开统计猫、狗以及猫、人的关系。最后统计一下就行了。

第一种,复杂度其实太高了(O(n^3*2^(2n))),但是可以卡过。第二种复杂度O(n^3*2^n),是比较靠谱的。


//#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef long long LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const int INF=1000111222;
const double INFF=1e200;
const double eps=1e-8;
const int mod=1000000007;
const int NN=205;
const int MM=10010;
/* ****************** */

int mat1[15],mat2[15];
//mao->ren mao->gou
int dp[12][1<<10][12];
int l[1<<10],r[1<<10];

void DP(int n,int m,int limit,int* mat,int* temp)
{
    memset(dp,0,sizeof(dp));
    int i,j,k,k1,t;
    int mask=(1<<m);
    dp[0][0][0]=1;

    for(i=0;i<n;i++)
    {
        for(j=0;j<mask;j++)
            for(k=0;k<=limit;k++)
                if(dp[i][j][k])
                {
                    dp[i+1][j][k]+=dp[i][j][k];
                    for(k1=0;k1<m;k1++)
                    {
                        t=1<<k1;
                        if(t&mat[i+1])
                            continue;
                        if(t&j)
                            continue;
                        dp[i+1][j+t][k+1]+=dp[i][j][k];
                    }
                }
    }

    for(j=0;j<mask;j++)
        temp[j]=dp[n][j][limit];
}

LL solve(int n,int m,int p)
{
    DP(n,m,n,mat1,l);
    DP(p,m,n,mat2,r);
    int i,mask=1<<m;
    
    LL ans=0;
    for(i=0;i<mask;i++)
    {
        ans+=(LL)l[i]*r[i];
    }
    return ans;
}

int main()
{
    int n,m,p,q;
    int x,y;
    LL ans;
    while(scanf("%d%d%d",&n,&m,&p)!=EOF)
    {
        scanf("%d",&q);

        memset(mat2,0,sizeof(mat2));
        memset(mat1,0,sizeof(mat1));

        while(q--)
        {
            scanf("%d%d",&x,&y);
            if(x>y)
                swap(x,y);
            //mao->gou
            if(x>n)
            {
                mat2[y-n-m]|=1<<(x-n-1);
            }
            else
            {
                mat1[x]|=1<<(y-n-1);
            }
        }

        ans=solve(n,m,p);
        cout<<ans<<endl;
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值