-
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; }
G - Party CodeForces - 906C -状态压缩+DFS
猜你喜欢
转载自blog.csdn.net/BePosit/article/details/83821282
今日推荐
周排行