G - Party CodeForces - 906C -状态压缩+DFS

  • G - Party

  •  CodeForces - 906C 
  • 题意:给出一些关系都是双人关系而且无向,可以进行一步操作,每一步操作是选择一个人,
  • 让这个人认识关系群里面的人都相互认识,求一个最小操作数
  • 思路:第二次碰到这样的状态压缩题目了,把一个人所认识其他人用二进制来表示1<<n-1为则表示这个人认识n个人
  • 因为1<<n-1 二进制有n为全为1,就是说二进制的位置代表第几个朋友,0表示不认识,1表示 认识
  • 则最初初始化为 认识自己 即每一个人的最初二进制关系网为 1<<i,这里注意因为二进制中1左移0位为1,
  • 所以所有人的编号要减一即还是n个人把原来的1-n变为了0-n-1 这样 正好与二进制相适应
  • 最核心的就是 DFS过程了,代码中注释的小细节不再解释,总题思路就是,刚开始copy了n个最初的关系网
  • 最后通过第n层的关系网络检测这种操作是否是有效(所有人的关系网都为(1<<n)-1)操作,
  • (并且有一个如果比已知答案更差的就return的剪枝过程)
  • 由于是先递归到底在往前回溯所以不会对最前面的关系网产生破坏,而后面的关系网改变后还可以通过
  • 每一次都有一个 memcpy(dp[x+1],dp[x],sizeof(dp[x]));进行更新每一种选择
  • 每次x<n的dfs进入后有两种选择一直不选择这个人直接往下dfs(x+1),另一种是选择这个人,把这个人认识的人
  • 全部更新到下一层。然后这样搜索最优解即可,
  • #include<bits/stdc++.h>
    using namespace std;
    #define maxn 25
    int dp[maxn][maxn];
    stack<int>ans,qyn;
    int n,m,u,v;
    void dfs(int x)
    {
        if(qyn.size()>=ans.size())return ;//更差的情况进行剪枝
        if(x==n)
        {
            for(int i=0; i<n; i++)
                if(dp[x][i]!=(1<<n)-1)return ;
            //判断是否是一种成功让所有人都认识的情况
            ans=qyn;
            return ;
        }
        memcpy(dp[x+1],dp[x],sizeof(dp[x]));
        dfs(x+1);
        for(int i=0; i<n; i++)
            if(dp[x][x]&(1<<i))dp[x+1][i]|=dp[x][x];
        qyn.push(x+1);
        dfs(x+1);
        qyn.pop();
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=0; i<n; i++)
        {
            ans.push(i+1);
            dp[0][i]=1<<i;
        }
        while(m--)
        {
            scanf("%d%d",&u,&v);
            u--;
            v--;
            dp[0][u]|=1<<v;
            dp[0][v]|=1<<u;
        }
        dfs(0);
        printf("%d\n",ans.size());
        while(!ans.empty())
        {
            if(ans.size()==1)
                printf("%d\n",ans.top());
            else
                printf("%d ",ans.top());
            ans.pop();
        }
        return 0;
    }

猜你喜欢

转载自blog.csdn.net/BePosit/article/details/83821282
今日推荐