队伍统计

1月27日

Description

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

Input

输入文件名为count. in。
第一行包括三个整数n,m,k。
接下来m行,每行两个整数u,v,描述一个矛盾关系(u,v)。
保证不存在两对矛盾关系(u,v),(x,y),使得u=x且v=y 。

Output

输出文件名为count.out。
输出包括一行表示合法的排列数。

Sample Input

输入1:

4 2 1
1 3
4 2

输入2:

10 12 3
2 6
6 10
1 7
4 1
6 1
2 4
7 6
1 4
10 4
10 9
5 9
8 10

Sample Output

输出1:

18

输出2:

123120

Data Constraint

对于30%的数据, n ≤ 10 n\leq 10 n10

对于60%的数据, n ≤ 15 n\leq 15 n15

对应100%的数据,n, k ≤ 20 k\leq 20 k20 , m ≤ n × ( n − 1 ) m\leq n \times (n-1) mn×(n1),保证矛盾关系不重复。

Solution

  • 【我的解法】

无脑暴力,30分,可以欣赏一下

#include<cstdio>
#pragma GCC optimize(3)//咳咳,别管这是什么
using namespace std;
const int mod=1000000007;

int n,m,k,tot;
int g[21][21];
int a[21];
bool vis[21];

void dfs(int x)
{
	if(x>n)
	{
		int cnt=0;
		for(int i=1;i<n;++i)
			for(int j=i+1;j<=n;++j)
				cnt+=g[a[i]][a[j]];
		if(cnt<=k)
		{
			tot++;
			tot%=mod;
		}
		return;
	}
	for(int i=1;i<=n;i++)
	{
		if(vis[i]) continue;
		a[x]=i;
		vis[i]=true;
		dfs(x+1);
		vis[i]=false;
	}
}

int main()
{
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1,u,v;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		g[v][u]=1;
	}
	dfs(1);
	printf("%d",tot);
	fclose(stdin);
	fclose(stdout);
	return 0;
}
  • 【大佬正解】

首先把状态压缩一下,把队伍压缩成一个二进制,由于n最大只有20,所以 2 20 2^{20} 220 是不会MLE的。
然后从前往后递推。f(s,i)表示当前选了s集合这些人,i表示违反了多少条矛盾关系。
然后如果暴力转移的话,是会炸的。 O ( 2 n n 2 k ) O(2^nn^2k) O(2nn2k)。所以要进行一些非(sang)常(xin)基(bing)础(kuang)的优化。
因为矛盾是没有重复的,所以把要在x前面的连个链式前向星,然后往左位移再&一下就可以了。

(其实有更优方法,但我不会调。。。只要维护一个fal[x]表示要排在x后面的人的集合,那么每次只要求出s & fal[x]的1的个数就能快速更新?了。)

#include<cstdio>
//#pragma GCC optimize(3)//不敢了不敢了再也不敢吸臭氧了
#define mod 1000000007
using namespace std;

int n,m,k,tot,ans;
int f[2097153][21];
int head[21],to[401],next[401],dive;

void add(int x,int y)
{
	++dive;
	next[dive]=head[x];
	to[dive]=y;
	head[x]=dive;
}
inline int read()//不吸臭氧就只能快读了QAQ
{
	int x=0; char c=getchar();
	while (c<'0' || c>'9') c=getchar();
	while (c>='0' && c<='9')
	{
		x=(x<<1)+(x<<3)+(c^48);
		c=getchar();
	}
	return x;
}

int main()
{
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	n=read();
	m=read();
	k=read();
	for(int i=1,u,v;i<=m;i++)
	{
		u=read();
		v=read();
		add(v,u);
	}
	f[0][0]=1;
	tot=(1<<n)-1;
	for(int s=0;s<=tot;s++)
		for(int i=0;i<=k;i++)
		{
			if(!f[s][i]) continue;
			for(int x=1;x<=n;x++)
			{
				if(s&(1<<x-1)) continue;
				int c=0;
				for(int p=head[x];p;p=next[p])
					if(!(s&(1<<to[p]-1))) c++;
				if(i+c>k) continue;
				f[s|(1<<x-1)][i+c]+=f[s][i];
				f[s|(1<<x-1)][i+c]%=mod;
			}
		}
	for(int i=0;i<=k;i++)
	{
		ans+=f[tot][i];
		ans%=mod;
	}
	printf("%d\n",ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值