[D - Peaceful Teams ](https://atcoder.jp/contests/abc310/tasks/abc310_d)

文章描述了一个编程问题,其中涉及将N个运动员分成T个团队,但有M对运动员之间存在不兼容,不能在同一团队。目标是找出满足条件的分组方案数量。提供的超时代码使用深度优先搜索策略,但由于状态空间大导致效率低下。文章提出了剪枝优化策略,但仍有改进空间。
摘要由CSDN通过智能技术生成

D - Peaceful Teams

Time Limit: 2 sec / Memory Limit: 1024 MB
Score : 400 points
Problem Statement
There are N N N sports players.Among them, there are M M M incompatible pairs. The
i i i-th incompatible pair ( 1 ≤ i ≤ M ) (1≤i≤M) (1iM) is the A i A_i Ai-th and B i B_i Bi-th players.
You will divide the players into T T T teams. Every player must belong to exactly one team, and every team must have one or more players. Additionally, for each i = 1 , 2 , … , M i=1,2,…,M i=1,2,,M, the A i A_i Ai -th and B i B_i Bi-th players must not belong to the same team.
Find the number of ways to satisfy these conditions. Here, two divisions are considered different when there are two players who belong to the same team in one division and different teams in the other.
Constraints
1 ≤ T ≤ N ≤ 10 1≤T≤N≤10 1TN10
0 ≤ M ≤ N ( N − 1 ) / 2 0≤M≤N(N−1)/2 0MN(N1)/2
1 ≤ A i < B i ≤ N ( 1 ≤ i ≤ M ) 1≤A_i<B_i≤N (1≤i≤M) 1Ai<BiN(1iM)
( A i , B i ) ≠ ( A j , B j ) ( 1 ≤ i < j ≤ M ) (A_i,B_i)≠(A_j,B_j) (1≤i<j≤M) (Ai,Bi)=(Aj,Bj)(1i<jM)
All input values are integers.
Input:
The input is given from Standard Input in the following format:
N N N T T T M M M
A 1 A_1 A1 B 1 B_1 B1
A 2 A_2 A2 B 2 B_2 B2
⋮ ⋮
A M A_M AM B M B_M BM
Output
Print the answer in a single line.
Sample Input 1
5 2 2
1 3
3 4
Sample Output 1
4
样例1图例分析
The following four divisions satisfy the conditions.
No other division satisfies them, so print 4.
Sample Input 2
5 1 2
1 3
3 4
Sample Output 2
0
There may be no division that satisfies the conditions.
Sample Input 3
6 4 0
Sample Output 3
65
There may be no incompatible pair.
Sample Input 4
10 6 8
5 9
1 4
3 8
1 6
4 10
5 7
5 6
3 7
Sample Output 4
8001

共有 N N N 位运动员,需要将他们分为 T T T 组(当然每一组不能为空),但是有 M M M 对运动员不能与对应的运动员分在同一组,试问有几种分队方案。
需要注意的是,队伍完全不讲究顺序,如:
方案1:
队伍1:运动员1,运动员3
队伍2:运动员2

方案2:
队伍1:运动员2
队伍2:运动员3,运动员1
以上两种方案均视为同一种方案。

基本思路是搜索。但是因为不能枚举重复方案(内容一样,顺序不一样)的问题,需要:
1.按顺序枚举每一个运动员;
2.在枚举完所有可能后,设真正有 x x x 种不同的方案,那么枚举出来有 x ! x! x! 种方案(排列问题,从 x x x 中选 x x x 个,讲究顺序,即 A x x A_x^x Axx 或者 P x x P_x^x Pxx)。
所以应该将答案除以 x ! x! x!
另外,对于冲突的队员,因为同一个人可能有很多与其冲突的队员,所以我选择用vector存储。
而大致的搜索思路如下:

dfs(当前队员序号){
	如果枚举完毕{
		判断每一个队是否为空,否则++ans;
		return;
	}
	将当前运动员尝试填入每一个队
		如果有与其冲突的队员已在里面,则continue;
}

下面是超时代码,49个点过45个, O ( n n ) O(n^n) O(nn)

#include <bits/stdc++.h>
#define int long long
using namespace std;
 
int n,t,vh[11],m,a[50],b[50],x[11][11],X[11],ans;
 
vector<int>c[11];
 
void dfs(int step){
	if(step>n){
		for(int i=1;i<=t;++i)
			if(X[i]==0)return;
		++ans;
		return;
	}
	for(int i=1;i<=t;++i){
		bool f=1;
		for(int j=0;j<c[step].size();++j){
			for(int k=1;k<=X[i];++k){
				if(c[step][j]==x[i][k]){
					f=0;
					break;
				}
			}
			if(!f)break;
		}	
		if(!f)continue;
		x[i][++X[i]]=step;
		dfs(step+1);
		--X[i];
	}
}
 
signed main(){
	cin>>n>>t>>m;
	for(int i=1;i<=m;++i){
		cin>>a[i]>>b[i];
		c[a[i]].push_back(b[i]);
		c[b[i]].push_back(a[i]);
	}
	dfs(1);
	for(int i=1;i<=t;++i)
		ans/=i;
	cout<<ans<<endl;
	return 0;
}

因为对于每一个问题节点,其状态不仅在递归参数列表里,还在状态数组里,所以无法记忆化或转递归(至少我不太行)。另外因为仅超时4个点,所以考虑剪枝。
什么样的一定不可行?
是空队数量大于还剩运动员数量。
所以加一个for循环(对于 n 2 n^2 n2 的复杂度这不算什么)计算空队数量,一旦大于,return,等于的话,应当是阶乘(不过优化小,就没加)。
最后1.6s卡过啦!

#include <bits/stdc++.h>
#define int long long
using namespace std;
 
int n,t,vh[11],m,a[50],b[50],x[11][11],X[11],ans;
 
vector<int>c[11];
 
void dfs(int step){
	if(step>n){
		for(int i=1;i<=t;++i)
			if(X[i]==0)return;
		++ans;
		return;
	}
	for(int i=1;i<=t;++i){
		bool f=1;
		for(int j=0;j<c[step].size();++j){
			for(int k=1;k<=X[i];++k){
				if(c[step][j]==x[i][k]){
					f=0;
					break;
				}
			}
			if(!f)break;
		}	
		if(!f)continue;
		x[i][++X[i]]=step;
		dfs(step+1);
		--X[i];
	}
}
 
signed main(){
	cin>>n>>t>>m;
	for(int i=1;i<=m;++i){
		cin>>a[i]>>b[i];
		c[a[i]].push_back(b[i]);
		c[b[i]].push_back(a[i]);
	}
	dfs(1);
	for(int i=1;i<=t;++i)
		ans/=i;
	cout<<ans<<endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GaoGuohao2022

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值