Luogu P1231 教辅的组成

  一道比较简单的的网络流拆点题目。

  首先考虑这样一种建模的想法:建超级源点S,超级汇点T,将源点与所有练习册相连,再将所有答案与汇点相连。

  然后根据各练习册,书本之间的关系进行连边。然后一脸期待的跑Dinic,结果很干脆:WA

  但是,为什么?

  考虑这样一张图(S标为0,T标为N1+N2+N3+1,书为1到N1,练习册为N1+1到N1+N2,答案为N1+N2+1到N1+N2+N3)

  

  对于上面的图,如果跑最大流会得到2.

  但是我们发现,这样标号为1的书被使用了两次,这是不允许的!

  因此我们发现每一个点的流量限制均为1,那该怎么办呢?

  很简单,我们把一个点拆成两个,一个连入度的边,一个连出度的边,然后在两条边之间连一条容量为1的边,就可以完美地解决这个问题。

  CODE

#include<cstdio>
#include<cstring>
using namespace std;
const int N=10005<<2,M=1000005,INF=1e9;
struct edge
{
    int to,next,c;
}e[M];
int head[N],Q[N],dep[N],n,m,p,q,x,y,s,t,k=-1;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch=tc();
    while (ch<'0'||ch>'9') ch=tc();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline void add(int x,int y,int z)
{
    e[++k].to=y; e[k].c=z; e[k].next=head[x]; head[x]=k;
}
inline int min(int a,int b)
{
    return a<b?a:b;
}
inline bool BFS(void)
{
    memset(dep,0,sizeof(dep));
    Q[1]=s; dep[s]=1;
    int H=0,T=1;
    while (H<T)
    {
        int now=Q[++H];
        for (register int i=head[now];i!=-1;i=e[i].next)
        if (!dep[e[i].to]&&e[i].c)
        {
            dep[e[i].to]=dep[now]+1;
            Q[++T]=e[i].to;
        }
    }
    return dep[t];
}
inline int DFS(int now,int dist)
{
    if (now==t) return dist;
    int res=0;
    for (register int i=head[now];i!=-1&&dist;i=e[i].next)
    if (dep[e[i].to]==dep[now]+1&&e[i].c)
    {
        int dis=DFS(e[i].to,min(dist,e[i].c));
        dist-=dis; res+=dis;
        e[i].c-=dis; e[i^1].c+=dis;
    }
    if (!res) dep[now]=0;
    return res;
}
inline int Dinic(void)
{
    int sum=0;
    while (BFS()) sum+=DFS(s,INF);
    return sum;
}
int main(void)
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i;
    memset(e,-1,sizeof(e));
    memset(head,-1,sizeof(head));
    read(n); read(p); read(q); s=0; t=2*n+p+q+1;
    for (i=1;i<=p;++i)
    add(s,i,1),add(i,s,0);
    for (i=1;i<=q;++i)
    add(i+p,t,1),add(t,i+p,0);
    for (i=1;i<=n;++i)
    add(i+p+q,i+n+p+q,1),add(i+n+p+q,i+p+q,0);
    read(m);
    for (i=1;i<=m;++i)
    {
        read(x); read(y);
        add(y,x+p+q,1); add(x+p+q,y,0);
    }
    read(m);
    for (i=1;i<=m;++i)
    {
        read(x); read(y);
        add(x+n+p+q,y+p,1),add(y+p,x+n+p+q,0);
    }
    printf("%d",Dinic());
    return 0;
}

  另外,这道题和Luogu P1402 酒店之王是完全相同的建模形式(数据范围还缩小了),可以双倍经验

原文地址:https://www.cnblogs.com/cjjsb/p/8659810.html