$のPOJ〜1966〜ケーブルテレビ〜〜ネットワーク$
$ソリューション:$
一見それが困難起動することがありますが、これはネットワークストリームに向けられているので、私たちぶっきらぼう。私たちは連結グラフが切断されるようにする必要があり、この質問、何になるしないように2つの点を持つようにバインドされ、データの範囲のこの質問が小さいので、我々は暴力の2点を列挙してみてください。これは、最小カットになります。しかし、ああ?ポイントはどのように何かをカットするのですか?
私たちが学んできた知識を閉じるために、我々は最先端になることができます無料、カットポイントを見たいのですが。とにかく、モデリングに関するほとんどのように刻々と変化するネットワークフロー。。。だから私たちは本のものをご紹介します:
- ノードは、二つのノードに元のノードエッジで表される中間体を分割することができます
- 一方の側は両側に分割することができ、元のエッジが中間点を表します。
- 右側は、点の中央がすぐ隣のエッジに切れ目が、影響INFを除外するかどうかで表す(これを遮断することができないため)
私たちは、この問題を解決することができる第一および第三のプロパティを使用します。まず、2 $ I $ $ I + N $と各ノードのノード。このエッジが最小カットで切断される場合、有向エッジの重みによって2つのノード間の値($ I $ iを$からは+ N $)を、(原点に相当するが切断されますアウト)。$ノードは、その後縁(重み正の無限大)に接続されているI $ iは、n個の$ノード発信エッジ(重み正の無限大)を+ $、正の無限大であってものみ中央側エッジを作るために切断されます。その後、我々は1の重量ので、再び元の各ポイントに代わって最小カットに対応し、最大流量を、それを実行し、そのトラフィックが答えです。
注:実際のソースネットワークフローは、$ S + N $、偶数アウトサイドである我々は、ソースとシンクの点は、2つの点に分割されています。彼らは、中間エッジに接続されていないようので、ソースとシンク点の性質は、これらの2つの点は、遮断することができません。
$コード:$
#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define ll long long
#define db double
#define rg register int
using namespace std;
int n,m,S,T;
int ans,top=1;
int dep[505];
int tou[505];
int qi[505];
int f[55][55];
struct su{
int to,v,next;
}b[5005];
inline int qr(){
register char ch; register bool sign=0; rg res=0;
while(!isdigit(ch=getchar()))if(ch=='-')sign=1;
while(isdigit(ch))res=res*10+(ch^48),ch=getchar();
if(sign)return -res; else return res;
}
inline void add(int x,int y,int v){ //注意博主加边自带反向
b[++top]=su{y,v,tou[x]}; tou[x]=top;
b[++top]=su{x,0,tou[y]}; tou[y]=top;
}
inline bool bfs(int x){
for(rg i=1;i<=x;++i)
qi[i]=tou[i],dep[i]=0;
queue<int> q; q.push(S); dep[S]=1;
while(!q.empty()){
rg i=q.front(); q.pop();
for(rg j=tou[i];j;j=b[j].next)
if(b[j].v&&!dep[b[j].to]){
dep[b[j].to]=dep[i]+1;
if(b[j].to==T)return 1;
q.push(b[j].to);
}
} return 0;
}
inline int dfs(int i,int w){
if(i==T||!w)return w;
rg rest=w,f;
for(rg &j=qi[i];j;j=b[j].next){
if(b[j].v&&dep[b[j].to]==dep[i]+1){
f=dfs(b[j].to,min(w,b[j].v));
if(!f){dep[b[j].to]=-2; continue;}
b[j].v-=f; b[j^1].v+=f; w-=f;
} if(!w)break;
}return rest-w;
}
inline void solve(){
rg res=0; top=1;
for(rg i=1;i<=n*2+2;++i) tou[i]=0; //初始化
for(rg i=1;i<=n;++i){
if(i!=S&&i!=T)add(i,i+n,1); //一点拆成两点,中间连边
for(rg j=1;j<=n;++j)
if(f[i][j])add(i+n,j,1e9); //连边注意是否有加n操作
} S=S+n;
while(bfs(n*2+2)) res+=dfs(S,1e9); //DInic
ans=min(res,ans);
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
rg t=qr();
while(t--){
n=qr();m=qr();
for(rg i=1;i<=n;++i){
for(rg j=i;j<=n;++j){
f[i][j]=f[j][i]=0; //初始化
}
}
for(rg i=1;i<=m;++i){
rg x=qr()+1,y=qr()+1;
f[x][y]=f[y][x]=1; //邻接矩阵读边
}
if(n==0||n==2){puts("0");continue;}
if(m==0&&n&&n!=2){puts("1");continue;}//特判,这题有点卡细节
ans=1e9;
for(rg i=1;i<=n;++i){
for(rg j=1;j<=n;++j){
if(f[i][j]||i==j)continue; //注意两个相邻的点不可能通过割点不联通
S=i;T=j; solve(); //枚举源汇点
}
} if(ans==1e9)ans=n; //无论怎么割点图都联通,就输出n
printf("%d\n",ans);
}
return 0;
}