HDU 5305 Friends (DFS,穷举+剪枝)

时间:2021-03-12 16:41:55

题意:

  给定n个人,m对朋友关系,如果对于每个人,只能刚好选择其所有朋友中的一半的人进行聊天(只是我和我的朋友,不是我的朋友和我的朋友),那么有多少种情况?只要一个选择不同,视为不同情况。

思路:

  比如我在14个朋友中选择了7个跟我聊天,那么另外7人已经完全与我没干系,而和我聊天的7个朋友,也已经和我聊天了,即我们配对了,共7对,他所选择的那一半的人中也必须有我。

  其实只考虑所给的m条边就行了。如果是奇数对关系,必定有人是奇数个朋友,那么也就0种情况。如果是偶数条边,还得判断每个人是否都是偶数个朋友,若不是也是0种。

  满足了情况之后再对m个关系选取其中的m/2条即可。但是所选的关系也必须是满足要求的,那么对于所选的m/2条关系进行判断即可知道是否满足要求,穷举所有可能进行判断。DFS就可以了,每条边要么选,要么不选。但是必须剪枝才能过。

 #include <bits/stdc++.h>
#define LL long long
#define pii pair<int,int>
#define INF 0x7f7f7f7f
using namespace std;
const int N=;
int n, m, s[], e[], num, times[N], du[N];
int DFS(int x) //x是第几条边
{
if( num*>=m ) //已经够一半了,判断是否满足要求
{
for(int i=; i<=n; i++) if( *du[i]!= times[i] ) return ; //每个人的度有一半即可。
return ;
} int ans=;
if( du[ s[x] ]*<times[ s[x] ] && du[ e[x] ]* <times[e[x]] ) //剪枝:这条边两个端点都已经满度,就不能再选了。
{
du[s[x]]++,du[e[x]]++;
num++; //所选边的数量
ans+=DFS(x+);
du[s[x]]--,du[e[x]]--;
num--;
} if( m/-num < m-x ) //还没有决定是否选的边数必须不小于m的一半
ans+=DFS(x+);
return ans;
} int cal(int n )
{
//先检查是否满足奇数度的要求
if(m&) return ;
for(int i=; i<=n; i++) if( times[i]& ) return ; num=;
memset(du,,sizeof(du));
return DFS();
} int main()
{
//freopen("e://input.txt", "r", stdin);
int t;
cin>>t;
while(t--)
{
memset(times, , sizeof(times));
scanf( "%d%d",&n,&m );
for(int i=; i<m; i++)
{
scanf("%d%d",&s[i],&e[i]);
times[s[i] ]++; //记录朋友个数
times[e[i] ]++;
}
printf("%d\n",cal(n));
}
return ;
}

AC代码