HDU 5305

HDU 5305

题意是有n个人,m对朋友关系,朋友可以使在线朋友和离线朋友,对于单个人,他的在线朋友和离线朋友数必须一致,请问有几种方案。
题目中n并不大,不超过8,那么暴力一点,用搜索,穷尽所有方案并判断是否可行,求出可行方案数。
为了使搜索更加高效,我们先进行状态压缩,bin(x0,x1,x2…xn)表示一个人的朋友情况,xi位为1表示与i是在线朋友,转化成十进制保存在sta数组。先预处理出,在X个人中选出Y个人的方案,保存在P[X][Y]中。计算出每个人有多少个朋友,保存在cnt数组。
从第一个人开始深搜,枚举P[n][cnt[1]/2]的所有方案,并根据朋友关系改变相应朋友的sta值。第二个人在第一个的基础上枚举P[n-1][cnt[2]/2-has],has表示已经拥有的在线朋友数,同理,根据方案改变后面的人的sta值…记得回溯时将后面的人的sta值改成原来的值。

#include<map>
#include<cmath>
#include<ctime>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<string>
#define LL long long
using namespace std;
const int maxn=1005;
const LL Inf=1e18;
vector<int> Next[10];
vector<int> P[10][10];
int M[10], cnt[10], sta[10];;
int n, m;
int ans;
int bitcount(int x){
    int ret=0;
    while(x){
        if(x&1) ret++;
        x>>=1;
    }
    return ret;
}
void pre(){
    for(int i=1;i<=8;i++)
        for(int j=0;j<(1<<i);j++){
        int bits=bitcount(j);
        P[i][bits].push_back(j);
    }
}
void dfs(int u){
    int has=bitcount(sta[u]);
    if(2*has>cnt[u]) return;
    if(n-u<cnt[u]/2-has) return;
    if(u==n){
        ans++;
        return;
    }
    int left=n-u, need=cnt[u]/2-has;
    for(int i=0;i<P[left][need].size();i++){
        int x=P[left][need][i];
        if(((x<<u)|M[u])!=M[u]) continue;
        for(int j=0;j<left;j++)
        if(x&(1<<j))
            sta[u+j+1]|=(1<<(u-1));
        dfs(u+1);
        for(int j=0;j<left;j++)
        if(x&(1<<j))
            sta[u+j+1]^=(1<<(u-1));
    }
}
void solve(){
    for(int i=1;i<=n;i++)
    if(cnt[i]%2){
        puts("0");
        return;
    }
    for(int i=1;i<=n;i++){
        M[i]=0;
        for(int j=0;j<Next[i].size();j++){
            M[i]|=(1<<(Next[i][j]-1));
        }
    }
    ans=0;
    memset(sta,0,sizeof(sta));
    dfs(1);
    printf("%d\n",ans);
}
int main(){

//      freopen("matrix.in","r",stdin);//从in.txt中读取数据
//      freopen("matrix.out","w",stdout);//输出到out.txt文件
    int T;
    int u, v;

    pre();
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n, &m);
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<=n;i++)
            Next[i].clear();
        for(int i=1;i<=m;i++){
            scanf("%d%d",&u,&v);
            cnt[u]++, cnt[v]++;
            Next[u].push_back(v);
            Next[v].push_back(u);
        }
        solve();
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值