在虚拟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;
}