UPC-组合数

学习犹如逆水行舟,不进则退

组合数

题目描述

从 1 到 N 的整数中挑选一些数,组成集合的方案数是可算的。如果加上 M 个限制:某 2 个数不能 同时选又要怎样计算?

输入

第一行包含 2 个整数 N 和 M,1≤N≤20,1≤M≤400。 下面 M 行,每行 2 个不同的整数 a 和 b 表示这 2
个数不能同时选。1≤a,b≤N,有些限制可能出现多次。

输出

一个整数。

Sample Input

3 2
1 2
2 3

Sample Output

5

扫描二维码关注公众号,回复: 10444525 查看本文章

Sample Input

3 3
1 2
1 3
2 3

Sample Output

4

解题思路

首先看到这一题之后第一反应是看数据大小,因为这种题目不是组合数学就是暴力。很明显这是一个暴力的题目,因为才20个数据复杂度在2n左右,所以直接走暴力就没有问题。
反思
为何会 TLE ????

看到题目后,确定了是dfs,但是思路错了。没有意识到这是一个01dfs于是乎就想到的是构造树,通过剪枝来实现,但是忽略了实现与剪枝之间代码的复杂度,导致超时,为何不去想01dfs的更深层的原因是。。。
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
对这段程序复杂度的理解出了问题,错误的把n2理解成了n!的复杂度。在更正了与意识到了这些错误之后就可以写dfs了,然后就可以愉快的一把 Accepted !

AC时间到
首先代码贴上

#include<iostream>
#include<algorithm>
#include<string.h>
#include<map>
#include<string>
#include<math.h>
#include<stdio.h>
#pragma GCC optimize(2)
#define Swap(a,b)  a ^= b ^= a ^= b
using namespace std;
typedef long long ll;
const int MAXN=10010;
const ll long_inf=9223372036854775807;
const int int_inf=2147483647;
inline ll read() {
	ll c=getchar(),Nig=1,x=0;
	while(!isdigit(c)&&c!='-')c=getchar();
	if(c=='-')Nig=-1,c=getchar();
	while(isdigit(c))x=((x<<1)+(x<<3))+(c^'0'),c=getchar();
	return Nig*x;
}
bool dp[30][30];
bool save[30];
ll n,t;
ll ans;
void dfs(int t) {
	if(t==n){
		for(int i=1;i<=n;i++)
		if(save[i])
		{
			for(int j=1;j<=n;j++)
			if(save[j]&&dp[i][j])
			return ;
		}
		ans++;
		return ;
	}
	dfs(t+1);
	save[t+1]=1;
	dfs(t+1);
	save[t+1]=0;
}
int main() {
	n=read(),t=read();
	for(ll i=0; i<t; i++) {
		ll a,b;
		a=read(),b=read();
		dp[a][b]=1;
		dp[b][a]=1;
	}
	dfs(0);
	cout<<ans<<endl;
	return 0;
}

思路很明确,就是枚举二进制,枚举出来之后再判断就可以了,用一个数组判断(此dp非彼dp,就是一个judge数据)

思路扩展
其实这个dfs中判断是否存在的代码可以进行优化(但是不知道为啥测评反而更慢了??)
首先放代码

#include<iostream>
#include<algorithm>
#include<string.h>
#include<map>
#include<string>
#include<math.h>
#include<stdio.h>
#pragma GCC optimize(2)
#define Swap(a,b)  a ^= b ^= a ^= b
using namespace std;
typedef long long ll;
const int MAXN=10010;
const ll long_inf=9223372036854775807;
const int int_inf=2147483647;
inline ll read() {
	ll c=getchar(),Nig=1,x=0;
	while(!isdigit(c)&&c!='-')c=getchar();
	if(c=='-')Nig=-1,c=getchar();
	while(isdigit(c))x=((x<<1)+(x<<3))+(c^'0'),c=getchar();
	return Nig*x;
}
bool dp[30][30];
bool save[30];
ll n,t;
ll ans;
int main() {
	n=read(),t=read();
	for(ll i=0; i<t; i++) {
		ll a,b;
		a=read(),b=read();
		dp[a][b]=1;
		dp[b][a]=1;
	}
	int temp=(1<<n);
	int ans=0;
	for(int i=0; i<temp; i++) {
		for(int j=0; j<n; j++)
			if(i&(1<<j))
				for(int k=0; k<n; k++)
					if(i&(1<<k)&&dp[j+1][k+1])
						goto A;
		ans++;
A:
		;
	}
	cout<<ans<<endl;
	return 0;
}

这就是一般通解(枚举二进制)
实际上根据二进制的01问题就可以看出来该数字有没有出现,和用数组存一下是一样的效果。
二进制枚举其实就是用&这个运算符进行运算得以判断。
通过1<<n来判断第n+1位是true还是flase

赛后反思
经历了本次的失败,明白了学海无涯,学习犹如逆水行舟不进则退

书山有路勤为径, 学海无涯苦作舟。

By-轮月

发布了32 篇原创文章 · 获赞 12 · 访问量 1193

猜你喜欢

转载自blog.csdn.net/qq_35339563/article/details/104149228
今日推荐