hdoj 2208 唉,可爱的小朋友

题目:http://acm.hdu.edu.cn/showproblem.php?pid=2208

本题实在不会,有时间在研究。

以下是网友的AC,地址:http://yomean.blog.163.com/blog/static/189420225201162435134493/

/*

这道题,原来不是像网上某些题解说的那样是强连通。(例如当1和2可以玩,不可以和3玩。但2可以和3玩,这样情况如果是用强连通,那样这3个人都是在一个棵里面。)神牛说是K SAT算法(属于NP算法)。所以用暴力是比较好的。题目数据那么小。

思路:让每一个人都分别属于一个不同的集合。然后判断一个点可不可以放入一个集合里,这时候要找到那个集合的根,同时,要确保这个点不与集合里面的所有点有冲突。同时,一个点可能可以放入多个集合中,那样通过一个深搜,那样就可以枚举出所有的可能,再通过一个判断,就可以知道是否有满足题意的点。

*/



#include<iostream>

using namespace std;

bool map[20][20];

int N,M;

int root[20],finish;

void dfs(int n,int m)

{

    if(finish) return ;//找到满足题意的方案

    if(m>M)  return ;//需要的球超出了预算

    if(n==N)//人数达到了要求,且这时候球的数量一定在预算内,找到了可行方案

    {

        finish=1;

        return ;

    }

    for(int i=0; i!=n; i++)

    {

        if(root[i]!=i) continue;//这时,找一个集合的根

        int fg=1;

        for(int j=i; j!=n&&fg; j++) //判断该点是否可以放入到该集合内

            if(root[j]==i)   fg=map[j][n];

        if(fg)//fg为1的时候,则说明这个点可以放到该集合内

        {

            root[n]=i;//将这个点指向根i

            dfs(n+1,m);//人数加1,进入下一个递归

            root[n]=n;//将该点还原,以找到下一个该点的可能落脚点,以确保可以考虑到所有的可能

        }

    }

    dfs(n+1,m+1);//将这个点分到一个新的集合里(一开始觉得这个是多余的,可是仔细想想,假如点2与点1可以共存,而他们放进进同一个集合里,/可是点3可以与在之前集合的所有的点(除点1外)匹配,那样因为有点1所以,点3只可以另外开一个集合.同时,点4也可以和之前集合里面的所有点//(除点2外)匹配,点4也不可以和点3匹配,这时,也只能开多一个集合.所以这步是有必要的.(当1可以和2,5,6...2可以和1,3,5,6..当3可以和2,5,6当4可以和1,5,6这个例子就可以说明了...)

}



int main()

{

    int temp,temp1;

    while(scanf("%d%d",&N,&M)!=EOF)

    {

        memset(map,false,sizeof(map));

        finish=0;

        for(int i=0; i!=N; i++)

        {

            scanf("%d",&temp);

            root[i]=i;

            for(int j=0; j!=temp; j++)

            {

                scanf("%d",&temp1);

                map[i][temp1]=true;//对无冲突的点的组合标记为真

            }

        }

        dfs(0,0);

        if(M>=N||finish) puts("YES");

        else puts("NO");

    }

    return 0;

}

 

你可能感兴趣的:(朋友)