总之就是 | SP300 & UVA1660 Cable TV Network
前言
实际上这两道是基本上一样的((
但是为了洛谷的题解同步我就都贴过来了。
SP300
题意简述
题目给出 T 个图,每个图有 n 个结点,m 条边,求最少去掉几个结点可以使给出的图不连通。
前置芝士
最小割
解决“最少去掉几个结点可以使网络的源点和汇点不连通”的问题时,我们常用到这样一个概念:最小割。
给定一个网络 \(G=(V,E)\),设其源点为 S,汇点为 T。若一个边的集合 \(E' \subseteq E\) 被删掉后 S 和 T 不再联通,则称这个边集 \(E'\) 是该网络的割。
而最小割就定义为边集内边的容量之和最小的割。
最大流最小割定理
即:任意一个网络的最大流量等于最小割中边的容量之和。
简单来说就是最大流量 = 最小割容量。
思路简述
注意到,题目的所求和我们常用最小割解决的那个问题很像,于是我们考虑将当前图上的问题转化到网络上求解。
联想一下两者的关系:网络本质上也是一张图,只不过多了源点和汇点。
于是我们就有了将图转化为网络求解的思路:枚举源点和汇点!
这个想法很暴力,不过同样也带来了更少的思维量。
因此我们可以直接枚举源点和汇点,再用 Dinic 求网络最大流(最小割)来切掉这道题。
Code
看似代码很长,实际上都是非常基础的东西组合起来的,所以建议大家还是先自己写。
//头文件略
#define Heriko return
#define Deltana 0
#define Romano 1
#define S signed
#define LL long long
#define R register
#define I inline
#define CI const int
#define mst(a, b) memset(a, b, sizeof(a))
#define ON std::ios::sync_with_stdio(false)
using namespace std;
template<typename J>
I void fr(J &x) {...//快读略}
template<typename J>
I void fw(J x,bool k) {...//快输略}
template<typename J>
I J Hmin(J x,J y) {Heriko x<y?x:y;}
template<typename J>
I void Clear(queue<J> &x) {while(x.size()) x.pop();}
CI MXX=40005,NXX=105,ABXX=2505,inf=998244353;
struct node
{
LL nex,to,val;
}
r[MXX];
LL n,m,cnt,head[NXX],dis[NXX],now[NXX],maxflow,s,t;
queue<LL> q;
I void Ass_we_can(LL x,LL y,LL z)
{
r[++cnt].to=y,r[cnt].nex=head[x],r[cnt].val=z;head[x]=cnt;
r[++cnt].to=x,r[cnt].nex=head[y],r[cnt].val=0;head[y]=cnt;
}
I bool BFS()
{
mst(dis,0);
Clear(q);
q.push(s);dis[s]=1;now[s]=head[s];
while(q.size())
{
LL x=q.front();q.pop();
for(R LL i=head[x];i;i=r[i].nex)
{
LL y=r[i].to;
if(!dis[y] and r[i].val)
{
q.push(y);
dis[y]=dis[x]+1;
now[y]=head[y];
if(y==t) Heriko Romano;
}
}
}
Heriko Deltana;
}
LL Dinic(LL x,LL flow)
{
if(x==t) Heriko flow;
LL i,y,k,rst=flow;
for(i=now[x];i and rst;i=r[i].nex)
{
y=r[i].to;
if(r[i].val and dis[y]==dis[x]+1)
{
k=Dinic(y,Hmin(r[i].val,rst));
if(!k) dis[y]=0;
r[i].val-=k;
r[i^1].val+=k;
rst-=k;
}
}
now[x]=i;
Heriko flow-rst;
}
LL a[ABXX],b[ABXX],flow,ans,T;
S main()
{
// freopen("RNMTQ.in","r",stdin);
fr(T);
while(T--)
{
fr(n),fr(m);
for(R LL i=0;i<m;++i)
{
char s[55];LL j;
scanf("%s",s);
a[i]=b[i]=0;
for(j=1;s[j]!=',';++j) a[i]=a[i]*10+s[j]-'0';
for(j++;s[j]!=')';++j) b[i]=b[i]*10+s[j]-'0';
}
ans=inf;
for(s=0;s<n;++s)
for(t=0;t<n;++t)
if(s!=t)
{
mst(head,0);cnt=1;maxflow=0;flow=0;
for(R LL i=0;i<n;++i)
{
if(i==s or i==t) Ass_we_can(i,i+n,inf);
else Ass_we_can(i,i+n,1);
}
for(R LL i=0;i<m;++i) Ass_we_can(a[i]+n,b[i],inf),Ass_we_can(b[i]+n,a[i],inf);
while(BFS())
while((flow=Dinic(s,inf))) maxflow+=flow;
ans=Hmin(maxflow,ans);
}
if(n<=1 or ans==inf) ans=n;
fw(ans,1);
}
Heriko Deltana;
}
希望能给大家带来收获。
UVA1660 Cable TV Network
SP300和这题基本一样罢。
前置芝士
最小割
给定一个网络 \(G=(V,E)\),设其源点为 S,汇点为 T。若一个边的集合 \(E' \subseteq E\) 被删掉后 S 和 T 不再联通,则称这个边集 \(E'\) 是该网络的割。
而最小割就定义为边集内边的容量之和最小的割。
最大流最小割定理
即:任意一个网络的最大流量等于最小割中边的容量之和。
简单来说就是最大流量 = 最小割容量。
Dinic 求最小割
由上面的定理可得求最小割的容量实际上就是求网络最大流量,所以我们可以直接用 Dinic 去求网络最大流:
I bool BFS()
{
mst(dis,0);
Clear(q);
q.push(s);dis[s]=1;now[s]=head[s];
while(q.size())
{
LL x=q.front();q.pop();
for(R LL i=head[x];i;i=r[i].nex)
{
LL y=r[i].to;
if(!dis[y] and r[i].val)
{
q.push(y);
dis[y]=dis[x]+1;
now[y]=head[y];
if(y==t) Heriko Romano;
}
}
}
Heriko Deltana;
}
LL Dinic(LL x,LL flow)
{
if(x==t) Heriko flow;
LL i,y,k,rst=flow;
for(i=now[x];i and rst;i=r[i].nex)
{
y=r[i].to;
if(r[i].val and dis[y]==dis[x]+1)
{
k=Dinic(y,Hmin(r[i].val,rst));
if(!k) dis[y]=0;
r[i].val-=k;
r[i^1].val+=k;
rst-=k;
}
}
now[x]=i;
Heriko flow-rst;
}
题意简述
直接看 UVA 的 PDF 有点慢,于是我在这里挂一个 Vjudge 的链接
给你有 n 个结点,m 条边的图,求最少去掉几个结点可以使给出的图不连通。
题目有多组数据,也就是说有多个图。
思路简述
解决“最少去掉几个结点可以使网络的源点和汇点不连通”的问题时,我们常用到最小割,于是我们考虑转换求解。
联想一下两者的关系:网络本质上也是一张图,只不过就多了源点和汇点。
因此我们可以直接枚举源点和汇点,再用 Dinic 求网络最大流(最小割)来切掉这道题。
这个想法很暴力,不过同样也带来了更少的思维量,而更少的思维量意味着更我们写起代码来更加轻松愉悦~
Code
在放代码之前,发现这题在洛谷居然没有样例,于是我就把样例放在这里方便大家罢。
输入:
0 0
1 0
3 3 (0,1) (0,2) (1,2)
2 0
5 7 (0,1) (0,2) (1,3) (1,2) (1,4) (2,3) (3,4)
输出:
0
1
3
0
2
//头文件略
#define Heriko return
#define Deltana 0
#define Romano 1
#define S signed
#define LL long long
#define R register
#define I inline
#define CI const int
#define mst(a, b) memset(a, b, sizeof(a))
#define ON std::ios::sync_with_stdio(false)
using namespace std;
template<typename J>
I void fr(J &x) {...//快读略}
template<typename J>
I void fw(J x,bool k) {...//快输略}
template<typename J>
I J Hmin(J x,J y) {Heriko x<y?x:y;}
template<typename J>
I void Clear(queue<J> &x) {while(x.size()) x.pop();}
CI MXX=40005,NXX=105,ABXX=2505,inf=998244353;
struct node
{
LL nex,to,val;
}
r[MXX];
LL n,m,cnt,head[NXX],dis[NXX],now[NXX],maxflow,s,t;
queue<LL> q;
I void Ass_we_can(LL x,LL y,LL z)
{
r[++cnt].to=y,r[cnt].nex=head[x],r[cnt].val=z;head[x]=cnt;
r[++cnt].to=x,r[cnt].nex=head[y],r[cnt].val=0;head[y]=cnt;
}
I bool BFS() {...//BFS略}
LL Dinic(LL x,LL flow) {...//Dinic略}
LL a[ABXX],b[ABXX],flow,ans,T;
S main()
{
// freopen("RNMTQ.in","r",stdin);
while(scanf("%lld%lld",&n,&m)==2)//要注意本题的输入格式
{
for(R LL i=0;i<m;++i)
{
char s[55];LL j;
scanf("%s",s);
a[i]=b[i]=0;
for(j=1;s[j]!=',';++j) a[i]=a[i]*10+s[j]-'0';
for(j++;s[j]!=')';++j) b[i]=b[i]*10+s[j]-'0';
}
ans=inf;
for(s=0;s<n;++s)
for(t=0;t<n;++t)
if(s!=t)
{
mst(head,0);cnt=1;maxflow=0;flow=0;
for(R LL i=0;i<n;++i)
{
if(i==s or i==t) Ass_we_can(i,i+n,inf);
else Ass_we_can(i,i+n,1);
}
for(R LL i=0;i<m;++i) Ass_we_can(a[i]+n,b[i],inf),Ass_we_can(b[i]+n,a[i],inf);
while(BFS())
while((flow=Dinic(s,inf))) maxflow+=flow;
ans=Hmin(maxflow,ans);
}
if(n<=1 or ans==inf) ans=n;
fw(ans,1);
}
Heriko Deltana;
}
希望能给大家带来收获~