$bzoj1143-CTSC2008$ 祭祀$river$ 最小链覆盖-最大反链
作者:互联网
题面描述
在遥远的东方,有一个神秘的民族,自称\(Y\)族。他们世代居住在水面上,奉龙王为神。每逢重大庆典,\(Y\)族都会在水面上举办盛大的祭祀活动。我们可以把\(Y\)族居住地水系看成一个由岔口和河道组成的网络。每条河道连接着两个岔口,并且水在河道内按照一个固定的方向流动。显然,水系中不会有环流(下图描述一个环流的例子)。
由于人数众多的原因,\(Y\)族的祭祀活动会在多个岔口上同时举行。出于对龙王的尊重,这些祭祀地点的选择必须非常慎重。准确地说,\(Y\)族人认为,如果水流可以从一个祭祀点流到另外一个祭祀点,那么祭祀就会失去它神圣的意义。族长希望在保持祭祀神圣性的基础上,选择尽可能多的祭祀的地点。
输入格式
- 第一行包含两个用空格隔开的整数\(N≤10^2,M≤10^3\),分别表示岔口和河道的数目,岔口从\(1\)到\(N\)编号。
- 接下来\(M\)行,每行包含两个用空格隔开的整数\(u,v\),描述一条连接岔口\(u\)和岔口\(v\)的河道,水流方向为自\(u\)向\(v\)。
输出格式
- 第一行包含一个整数\(K\),表示最多能选取的祭祀点的个数。
题解
- 最小可交链覆盖(最大反链)
少有的思路比较清晰的题….- 首先看到\(N\leq 10^2\)就知道大概是个\(O(n^3)\)的算法,或者网络流。然后看题面给出的限制,要求两个祭祀点不能从一个流到另一个,意思就是对于一条链上只能有一个点,再想如果两条链可以互相通达也不可以。因此我们要求用最小的链数覆盖这张图。求最小可交链覆盖即可。
- 具体做法:先用\(floyd\)求出传递闭包,再把每个点拆成两个点(入点、出点)入点向出点连边,再按照传递闭包把一个点能连到的所有点都加边,边容量均为\(1\)。跑最大流\(maxflow\),答案就是\(n-maxflow\)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int MAXN=2e2+5;
const int MAXM=2e5+5;
const int inf=1e9;
int a[MAXN][MAXN];
int n,m,S,T;
int edge=1,head[MAXN],nex[MAXM],tail[MAXM],r[MAXM];
int d[MAXN],cur[MAXN];
queue<int> q;
void add(int u,int v,int R){
edge++,nex[edge]=head[u],head[u]=edge,tail[edge]=v,r[edge]=R;
}
void ins(int u,int v,int R){
add(u,v,R); add(v,u,0);
}
bool bfs(){
for (int i=1;i<=T;i++) d[i]=inf;
d[S]=0; q.push(S);
while (!q.empty()){
int u=q.front(); q.pop();
for (int e=head[u];e;e=nex[e]){
int v=tail[e];
if (d[v]==inf&&r[e]>0){
d[v]=d[u]+1;
q.push(v);
}
}
}
return d[T]<inf;
}
int dfs(int u,int b){
if (u==T) return b;
int ans=0;
for (int& e=cur[u];e;e=nex[e]){
int v=tail[e];
if (d[v]==d[u]+1&&r[e]>0){
int res=dfs(v,min(b,r[e]));
r[e]-=res; r[e^1]+=res;
b-=res; ans+=res;
}
if (!b) break;
}
return ans;
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++) a[i][j]=1e9;
}
for (int i=1;i<=m;i++){
int u,v; scanf("%d%d",&u,&v);
a[u][v]=1;
}
for (int k=1;k<=n;k++){
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
if (a[i][j]>a[i][k]+a[k][j]){
a[i][j]=a[i][k]+a[k][j];
}
}
}
}
S=2*n+2;T=S+1;
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
if (a[i][j]<inf&&i!=j) ins(2*i+1,2*j,1);
}
ins(S,2*i+1,1); ins(2*i,T,1);
// ins(2*i,2*i+1,1);
}
int flow=0;
while (bfs()){
for (int i=1;i<=T;i++) cur[i]=head[i];
flow+=dfs(S,inf);
}
printf("%d\n",n-flow);
return 0;
}
标签:include,祭祀,int,bzoj1143,最小链,edge,MAXN,反链,岔口 来源: https://www.cnblogs.com/shjrd-dlb/p/10983062.html