[POJ2942]:Knights of the Round Table(塔尖+二分图染色法)
题目传送门
题目描述
亚瑟王要在圆桌上召开骑士会议,为了不引发骑士之间的冲突,并且能够让会议的议题有令人满意的结果,每次开会前都必须对出席会议的骑士有如下要求:
1、相互憎恨的两个骑士不能坐在直接相邻的2个位置。
2、出席会议的骑士数必须是奇数,这是为了让投票表决议题时都能有结果。 如果出现有某些骑士无法出席所有会议(例如这个骑士憎恨所有的其他骑士),则亚瑟王为了世界和平会强制把他剔除出骑士团。
现在给定准备去开会的骑士数n,再给出m对憎恨对(表示某2个骑士之间使互相憎恨的),问亚瑟王至少要剔除多少个骑士才能顺利召开会议?
注意:
1、所给出的憎恨关系一定是双向的,不存在单向憎恨关系。
2、由于是圆桌会议,则每个出席的骑士身边必定刚好有2个骑士。即每个骑士的座位两边都必定各有一个骑士。
3、一个骑士无法开会,就是说至少有3个骑士才可能开会。
输入格式
输入包含多组测试。 每种情况都以包含两个整数1≤n≤1000且1≤m≤1000000的整数行开始。 数字n是骑士的数量。 接下来的m行描述哪个骑士憎恨哪个骑士。 这m行中的每一行包含两个整数k1和k2,这意味着骑士数k1和骑士数k2彼此讨厌(数字k1和k2在1和n之间)。
输入n=m=0时终止。
输出格式
对于每组测试,输出一个整数,表示必须被驱逐的骑士数量。
样例
样例输入:
5 5
1 4
1 5
2 5
3 4
4 5
0 0
样例输出:
2
题解
些许有些复杂,思维量较大。
注意亚瑟王会召开多次会议,可以参加其中任意一次会议的骑士就可以被保留。
考虑建出原图的补图,其定义为:原来相连的两个点现在不相连,原来不相连的两个点现在相连。
这样的话两个其实可以坐在一起的条件即为他们之间有连边,方便处理。
那么,如果一个骑士可以参加会议,当且仅当他在一个奇环里。
给出两个定理:
1、如果一个双连通分量内的某些顶点在一个奇圈中(即双连通分量含有奇圈),那么这个双连通分量的其他顶点也在某个奇圈中。
2、如果一个双连通分量含有奇圈,则他必定不是一个二分图。反过来也成立,这是一个充要条件。
显然利用塔尖求出每一个v-DCC,然后判断这个v-DCC是不是奇环即可。
这时就用到了交叉染色法,dfs时每一条边都和上一条边反色,当发现两条相邻的边同色时即为奇环。
特别注意:此题不能用万能头文件,否则会CE!!!
那些被CE打倒的大佬:
不过结局总会是好的:
代码时刻
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | #include<cstdio> #include<iostream> #include<vector> #include<cstring>//不要万能头!!! using namespace std; struct rec { int nxt; int to; }e[1000001]; int head[1001],cnt; int dfn[1001],low[1001],sta[1001],tot,top; bool par[1001],vis[1001]; int col[1001]; bool Map[1001][1001]; vector< int > dcc; void pre_work() //多测不清空,爆零两行泪TAT…… { cnt=0; tot=0; top=0; memset(head,0, sizeof (head)); memset(dfn,0, sizeof (dfn)); memset(low,0, sizeof (low)); memset(sta,0, sizeof (sta)); memset(vis,0, sizeof (vis)); memset(Map,0, sizeof (Map)); } void add( int x, int y) //建边 { e[++cnt].nxt=head[x]; e[cnt].to=y; head[x]=cnt; } bool dfs( int x, int color) //交叉染色法判奇环 { col[x]=color; for ( int i=head[x];i;i=e[i].nxt) { if (!par[e[i].to]) continue ; if (col[e[i].to]==color) return 1; if (!col[e[i].to]&&dfs(e[i].to,-color)) return 1; } return 0; } void color_solve() //将v-DCC转入数组,方便处理 { memset(par,0, sizeof (par)); memset(col,0, sizeof (col)); for ( int i=0;i<dcc.size();i++) par[dcc[i]]=1; if (dfs(dcc[0],1)) for ( int i=0;i<dcc.size();i++)vis[dcc[i]]=1; } void tarjan( int x) //塔尖 { dfn[x]=low[x]=++tot; sta[++top]=x; for ( int i=head[x];i;i=e[i].nxt) { if (!dfn[e[i].to]) { tarjan(e[i].to); low[x]=min(low[x],low[e[i].to]); if (dfn[x]<=low[e[i].to]) //发现v-DCC { int y; dcc.clear(); do { y=sta[top--]; dcc.push_back(y); } while (e[i].to!=y); dcc.push_back(x); color_solve(); } } else low[x]=min(low[x],dfn[e[i].to]); } } int main() { while (1) { int n,m; scanf( "%d%d" ,&n,&m); if (!n&&!m) break ; pre_work(); for ( int i=1;i<=m;i++) { int x,y; scanf( "%d%d" ,&x,&y); Map[x][y]=Map[y][x]=1; //临接矩阵存图 } for ( int i=1;i<=n;i++) for ( int j=i+1;j<=n;j++) if (!Map[i][j]){add(i,j);add(j,i);} //链式前项星建反图 for ( int i=1;i<=n;i++) if (!dfn[i])tarjan(i); int ans=0; for ( int i=1;i<=n;i++) //统计可以参加的骑士 if (vis[i])ans++; printf( "%d\n" ,n-ans); //用总骑士数减去 } return 0; } |
rp++
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· .NET Core内存结构体系(Windows环境)底层原理浅谈
· C# 深度学习:对抗生成网络(GAN)训练头像生成模型
· .NET 适配 HarmonyOS 进展
· 手把手教你更优雅的享受 DeepSeek
· 腾讯元宝接入 DeepSeek R1 模型,支持深度思考 + 联网搜索,好用不卡机!
· AI工具推荐:领先的开源 AI 代码助手——Continue
· 探秘Transformer系列之(2)---总体架构
· V-Control:一个基于 .NET MAUI 的开箱即用的UI组件库