[SP2878]KNIGHTS - Knights of the Round Table
阿新 • • 發佈:2020-08-19
[SP2878]KNIGHTS - Knights of the Round Table
一.前言
腦闊完全轉不動……都不知道要幹些什麼,題目連結
二.思路
首先它是圓桌騎士開會,所以他們開會的樣子是一個環,並且避免平票需要只有奇數個人(不能一人開會)。將仇恨關係建圖不好做,於是建立補圖,將可以坐在一起的騎士連邊,這樣形成了一個可能的座位形狀?然後去裡面找儘可能多的奇環。輸出不被任何一個奇環包括的騎士。
然後到這裡我們會有一個結論如下:
對於一個點雙連通分量,裡面若是有一個奇環,那麼該點雙連通分量裡面所有的點都可以被包括在奇環之中
雖然我也不知道為什麼會出現這個結論(好像寫邊雙也可)就象徵性的推導一下:
首先點雙連通分量由一些環組成
假設在點雙連通分量之中有環 a ,若 a 是奇環,那麼 a 中所有點都可以開會,不影響答案
若 a 是偶環,那麼將它和點雙中的奇環連結成一個大奇環就可以了。
我也不知道是不是對的hhh,感性理解一下……同樣我也不好解釋奇環和點雙的關係,硬要說的話,就是 tarjan 縮點之後不會有環,換而言之所有的環都在點雙連通分量之中,由於這個結論很方便,就這麼寫了
現在需要判斷一個點雙裡面有沒有奇環。對於一個沒有奇環的圖來說,他一定是一個二分圖(有了奇環就構不成二分圖),轉化為判斷一個點雙是不是二分圖,使用二分圖黑白染色,即將一個點周圍的點全部染上相異的顏色,有衝突就不是二分圖,也就是有奇環。
這樣寫就可以了。
三.CODE
#include<iostream> #include<cstdio> #include<algorithm> #include<fstream> #include<cmath> #include<cstring> using namespace std; int read(){ char ch=getchar(); int res=0,f=1; for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1; for(;ch>='0'&&ch<='9';ch=getchar())res=res*10+(ch-'0'); return res*f; } const int MAXN=1e6+5; int n,m; bool hate[1005][1005]; int head[1005],ne[MAXN],to[MAXN],tot; void add(int x,int y){ to[++tot]=y,ne[tot]=head[x],head[x]=tot; } void build(){ for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j) if(!hate[i][j])add(i,j),add(j,i); } int dfn[1005],low[1005],date; int st[1005],top,rs[1005]; bool vis[1005],fl,wdnmd[1005]; int ans; bool check(int x,int fa){ if(fl)return 1; for(int i=head[x];i;i=ne[i]){ int v=to[i]; if(v==fa||!vis[v])continue; if(rs[v]==-1){ rs[v]=rs[x]^1;//染色 check(v,x); if(fl)return 1; } else if(rs[v]!=(rs[x]^1))return (fl=1);//衝突 } return 0; } void dfs(int x,int fa){//tarjan dfn[x]=low[x]=++date; st[++top]=x; for(int i=head[x];i;i=ne[i]){ int v=to[i]; if(!dfn[v]){ dfs(v,x); low[x]=min(low[x],low[v]); if(low[v]>=dfn[x]){//出現點雙 int count=1; fl=0; memset(vis,0,sizeof(vis)); memset(rs,-1,sizeof(rs)); vis[x]=1;//將當前點雙之中的全部打上標記 rs[x]=0; while(st[top]!=v)vis[st[top--]]=1,count++; vis[v]=1,top--,count++; if(count>=3&&check(x,x))for(int i=1;i<=n;++i)if(vis[i])wdnmd[i]=1; //二分圖染色 } } else low[x]=min(low[x],dfn[v]); } } void clean(){ memset(hate,0,sizeof(hate)); memset(head,0,sizeof(head)); tot=0;ans=0; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(wdnmd,0,sizeof(wdnmd)); } int main(){ while(1){ n=read();m=read(); if(n==m&&n==0)break; clean();//多測清空 for(int i=1,x,y;i<=m;++i){ x=read();y=read(); hate[x][y]=hate[y][x]=1; } build();//建立補圖 for(int i=1;i<=n;++i)if(!dfn[i]){//可能會不連通,找點雙 memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); date=top=0; dfs(i,i); } for(int i=1;i<=n;++i)ans+=(wdnmd[i]==0);//加上不被點雙標記的 printf("%d\n",ans); } return 0; }