F. 小组论
Liserious最近迷上了密室逃脱,他总是在跪求他的小伙伴和他一起去玩各种各样的密室逃脱。
有一天他们遇到了一家非常复杂的密逃,为了获得其中一个关键道具,他们需要先站成一列,李希尔瑞斯的小伙伴们来自N个小组(比如一姐带领的“EEEat”;芳姐带领的“Doctooor”小组;51isoft带领的“Weeaaak”小组;力儿妹妹带领的“Mahhhjong”小组;龚犇带领的“Life Winnerrr”小组)。
现在每个人对自己的位置都有一个要求,要么他是他所在小组的队列里的第一个,要么他与他同一小组的前一个人的距离恰好为K。
Input
输入以EOF结束。
每组数据的第一行为一个整数N,表示小组的数量。
接下来N行,每行第一个整数M(M <= 5),表示这个小组的人数,接下来M个数,分别表示这M个人要求距离前一个同组的小伙伴的距离。
输入保证小伙伴的总数不超过20。
Output
对于每组数据,输出一个整数,表示总的排列的方案数。
Sample Input
2 1 1 2 1 2
Sample Output
3
思路:
这个题和这道题基本是一个套路:http://blog.csdn.net/mengxiang000000/article/details/60767512
我们知道,当n大的时候,M平均起来肯定小,也就是说对于每组的人的摆放的情况就会少很多,所以我们每次考虑两个组的合并。
设定Dp【i】表示已经排列出来状态为i的情况的方案数。
那么有:Dp【i】+=preDp【j】,这里i=j|X,这里X表示的是当前组成员的某种插入队列的方式。我们知道,当n大的时候,M平均起来肯定小,所以前期的情况数肯定灰常少,
所以我们只要注意常数就可以了。理论上是可以找到峰值操作次数的。
那么我们每一次将之前的排放方式和当前组的排放方式去合并Dp即可,注意常数。
Ac代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
using namespace std;
#define ll long long int
int tot,n;
int Dist[25][25];
int num[25];
int p[25];
ll dp[(1<<21)];
ll predp[(1<<21)];
vector<int>mp[25];
void Slove()
{
vector<int>pre,zhongjie;
memset(dp,0,sizeof(dp));
memset(predp,0,sizeof(predp));
pre.push_back(0);predp[0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<mp[i].size();j++)
{
int tmpi=mp[i][j];
for(int k=0;k<pre.size();k++)
{
int tmppre=pre[k];
if((tmppre&tmpi)>0)continue;
if(dp[tmppre+tmpi]==0)zhongjie.push_back(tmppre+tmpi);
dp[tmppre+tmpi]+=predp[tmppre];
}
}
pre.clear();
for(int j=0;j<zhongjie.size();j++)
{
predp[zhongjie[j]]+=dp[zhongjie[j]];
dp[zhongjie[j]]=0;
pre.push_back(zhongjie[j]);
}
zhongjie.clear();
}
printf("%lld\n",predp[(1<<tot)-1]);
}
int main()
{
while(~scanf("%d",&n))
{
tot=0;
for(int i=1;i<=24;i++)mp[i].clear();
for(int i=1;i<=n;i++)
{
int ki,cnt=1;
scanf("%d",&ki);
tot+=ki;
num[i]=ki;
for(int j=1;j<=ki;j++)scanf("%d",&Dist[i][j]);
}
for(int i=1;i<=n;i++)
{
int cnt=1;
for(int j=1;j<=num[i];j++)p[j]=j,cnt*=j;
while(cnt--)
{
next_permutation(p+1,p+1+num[i]);
for(int pos=tot-1;pos>=0;pos--)
{
int tmp=0;
int tmppos=pos;
for(int j=1;j<=num[i];j++)
{
if(j>1)tmppos-=Dist[i][p[j-1]];
tmp+=(1<<tmppos);
}
if(tmppos<0)continue;
mp[i].push_back(tmp);
}
}
}
Slove();
}
}