网络流小结&反思 不断补充ing

14 篇文章 0 订阅
11 篇文章 0 订阅

暑期培训结束之际,开始做网络流。

根据网上网络流的题目,断断续续做到了今天。

很少有能自己想到怎么构图的,有的虽然构图对了,但还是wa了。

还经常犯同样的错误。

为了改变这种状况,特写此文。

 

========================================

 

一个值得一提的是,在没有经过认真的思考之前尽量不要去搜题解或是看discuss。

在平时训练的时候,这些习惯没有太多好处。

从别人那里得到的总是不如自己想出来的映像深刻和令人兴奋。

看同校大神师兄的oj账号,都是有不少提交但没ac的题目,反观自己,一想不出就搜题解,看discuss,拿别人的ac程序暴力对拍。。。

这样虽然题过了,但是下次再碰到同类型的题是不是就能做出来了?这个也只有自己知道了。又或者比赛时写出了八九成却wa了,然后找不到bug,只能赛后再去”取经“?

仅供自勉。

 

========================================

 

求最大流用的是ISAP,模板来自NotOnlySuccess的博客(神犇一枚,代码相当飘逸,不过最近博客经常打不开,貌似空间到期了Orz)

 

首先是模板题:

 

hdu3549 Flow Problem

注意一下重边。

反思:

         memset第二个参数是要赋的值,记成是区间长度。。。

         我就是传说中抄模板都能抄错的神一样的男子。。。

浙大模板:

 

#include <cstring>
#include <cstdio>
#include <iostream>

using namespace std;

#define DEBUG(x) cout<<#x<<":"<<x<<endl;

const int Max=20;
int mat[Max][Max],flow[Max][Max];
int pre[Max],d[Max];
int n,m;

void input()
{
    memset(mat,0,sizeof(mat));
    int u,v,c;
    while(n--)
    {
        scanf("%d%d%d",&u,&v,&c);
        mat[u][v]+=c;
    }
}

const int INF=0x7fffffff;
int sta[Max],p,q;
int maxflow()
{
    int f=0,s,t,cur;
    s=1;
    t=m;
    memset(flow,0,sizeof(flow));
    while(1)
    {
        memset(d,0,sizeof(d));
        memset(pre,0,sizeof(pre));
        d[s]=INF;
        pre[s]=s;
        cur=s;
        for(p=q=0; p<=q&&!pre[t]; cur=sta[p++])
            for(int i=1,j; i<=m; i++)
                if(!pre[i]&&(j=mat[cur][i]-flow[cur][i]))
                    pre[sta[q++]=i]=cur,d[i]=min(d[cur],j);
                else if(!pre[i]&&(j=flow[i][cur]))
                    pre[sta[q++]=i]=-cur,d[i]=min(d[cur],j);

        if(!pre[t])break;

        for(int i=t; i!=s;)
            if(pre[i]>0)flow[pre[i]][i]+=d[t],i=pre[i];
            else flow[i][-pre[i]]-=d[t],i=-pre[i];//- -
    }
    for(int i=1; i<=m; i++)f+=flow[s][i];
    return f;
}

int main()
{
    int t,cas=0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&m,&n);
        input();
        printf("Case %d: %d\n",++cas,maxflow());
    }
    return 0;
}

 

 

 

NotOnlySuccess:

 

#include <cstdio>
#include <cstring>

#define CC(a,x) memset(a,x,sizeof(a))
#define FOR(i,j,k) for(int i=j;i<=k;i++)
#define FF(i,n) for(int i=1;i<=n;i++)

inline void checkmin(int &aug,int &maze)
{
    if(aug==-1||maze<aug)
        aug=maze;
}
#define M 20
int maze[M][M];
int gap[M],dis[M],pre[M],cur[M];
int sap(int s,int t,int nodenum)
{
    CC(cur,0);
    CC(dis,0);
    CC(gap,0);
    int u = pre[s] = s,maxflow = 0,aug = -1;
    gap[0] = nodenum;
    while(dis[s] < nodenum)
    {
loop:
        FOR(v,cur[u],nodenum) if(maze[u][v] && dis[u] == dis[v] + 1)
        {
            checkmin(aug,maze[u][v]);
            pre[v] = u;
            u = cur[u] = v;
            if(v == t)
            {
                maxflow += aug;
                for(u = pre[u]; v != s; v = u,u = pre[u])
                {
                    maze[u][v] -= aug;
                    maze[v][u] += aug;
                }
                aug = -1;
            }
            goto loop;
        }
        int mindis= nodenum-1;
        FF(v,nodenum) if(maze[u][v] && mindis> dis[v])
        {
            cur[u] = v;
            mindis= dis[v];
        }
        if((--gap[dis[u]])== 0)    break;
        gap[dis[u] = mindis+1] ++;
        u = pre[u];
    }
    return maxflow;
}

int main()
{
    int t,cas=0;
    int n,m,u,v,w;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        CC(maze,0);
        while(m--)
        {
            scanf("%d%d%d",&u,&v,&w);
            maze[u][v]+=w;
        }
        printf("Case %d: %d\n",++cas,sap(1,n,n));
    }
    return 0;
}

 

 

 

 

poj1273/hdu1532 Drainage Ditches   

 

#include <cstdio>
#include <cstring>

using namespace std;
const int Max=222;

#define FOR(i,j,k) for(int i = j;i < k;i++)
#define _FOR(i,j,k) for(int i = j;i <= k;i++)
#define RE(i,n) for(int i = 0;i < n;i++)
#define _RE(i,n) for(int i = 1;i <= n;i++)

int maze[Max][Max];
void input(int n)
{
    memset(maze,0,sizeof(maze));
    int s,e,c;
    while(n--)
    {
        scanf("%d%d%d",&s,&e,&c);
        maze[s][e] += c;
    }
}

int pre[Max],d[Max];
int isap(int s,int t,int n)
{
    int MaxFlow=0,u,aug=-1;
    memset(d,0,sizeof(d));

    pre[u = s] = s;
    while(d[s] < n)
    {

LOOP:
        _RE(v,n)if(maze[u][v]&&d[u]==d[v]+1)
        {

            if(aug==-1||maze[u][v]<aug)aug = maze[u][v];
            pre[v] = u;
            u = v;
            if(u==t)
            {
                MaxFlow += aug;
                for(u = pre[u]; v!=s; v = u,u = pre[u])
                {
                    maze[u][v] -= aug;
                    maze[v][u] += aug;
                }
                aug = -1;
            }
            goto LOOP;
        }
        int MinDis = n-1;
        _RE(v,n)if(maze[u][v]&&MinDis>d[v])
        {
            MinDis = d[v];
        }

        d[u] = MinDis+1;
        u=pre[u];

    }
    return MaxFlow;
}

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        input(n);
        printf("%d\n",isap(1,m,m));
    }
}

 

 

 

 

 

 

 

接下来的需要简单的构图:

 

poj1274  The Perfect Stall

 

题意:

           有N头奶牛和M个stalls ,每头奶牛都只有在特定的某个或某几个stalls 时才产奶,求最多能有多少头奶牛产奶。

做法:

           奶牛与所有它喜欢的stalls都连一条容量为1 的边。

           从源点到每头奶牛连一条容量为1的边。

           从每个stalls到汇点连一条容量为1的边。

 

#include <cstdio>
#include <cstring>

using namespace std;
const int Max=405;

#define FOR(i,j,k) for(int i = j;i < k;i++)
#define _FOR(i,j,k) for(int i = j;i <= k;i++)
#define RE(i,n) for(int i = 0;i < n;i++)
#define _RE(i,n) for(int i = 1;i <= n;i++)
#define SET(a,x) memset(a,x,sizeof(a))

int maze[Max][Max];
void input(int n,int m)
{
    SET(maze,0);
    int e,t;
    _RE(i,n)
    {
        scanf("%d",&t);
        while(t--)
        {
        scanf("%d",&e);
        e += n;
        maze[i][e] = 1;
        }
        maze[0][i]=1;
    }
    _FOR(i,n+1,n+m)
    maze[i][n+m+1]=1;
}

int pre[Max],d[Max],cur[Max],gap[Max];
int isap(int s,int t,int n)
{
    int MaxFlow = 0,u,aug = -1;
    SET(d,0);
    gap[0] = n;
    pre[u = s] = s;
    while(d[s] < n)
    {

LOOP:
        _FOR(v,cur[u],n)if(maze[u][v]&&d[u]==d[v]+1)
        {

            if(aug==-1||maze[u][v]<aug)aug = maze[u][v];
            pre[v] = u;
            u = cur[u] = v;
            if(u==t)
            {
                MaxFlow += aug;
                for(u = pre[u]; v!=s; v = u,u = pre[u])
                {
                    maze[u][v] -= aug;
                    maze[v][u] += aug;
                }
                aug = -1;
            }
            goto LOOP;
        }
        int MinDis = n-1;
        _RE(v,n)if(maze[u][v]&&MinDis>d[v])
        {
            cur[u]=v;
            MinDis = d[v];
        }
        if(--gap[d[u]]==0)break;
        gap[d[u] = MinDis+1]++;
        u=pre[u];

    }
    return MaxFlow;
}

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        input(n,m);
        printf("%d\n",isap(0,m+n+1,n+m+2));
    }
}

 


还写了一份二分匹配的:

 

 

#include <cstdio>
#include <cstring>

const int Max=220;
int maze[Max][Max];

int n,m;

void input()
{
    memset(maze,0,sizeof(maze));
    for(int u=1,v,t;u<=n;u++)
    {
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d",&v);
            maze[u][v]=1;
        }
    }
}

int linkx[Max],vis[Max];

bool hungry(int u)
{
    for(int v=1;v<=m;v++)
    if(maze[u][v])
    {
       if(vis[v])
       continue;
       vis[v]=1;
       if(linkx[v]==-1||hungry(linkx[v]))
       {
           linkx[v]=u;
           return true;
       }
    }
    return false;
}

int MaxMatch()
{
    int ret=0;
    memset(linkx,-1,sizeof(linkx));
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof(vis));
        if(hungry(i))
        ret++;
    }
    return ret;
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        input();
        printf("%d\n",MaxMatch());
    }
}

 

 

 

 

 

poj1698 Alice's Chance

题意:

          一部电影需要需要D天才能拍完,且只能在一周中的某几天拍摄,还必须在W周之内拍完。

          有N部这样的电影,问Alice能否N部电影都拍。

做法:

           每部电影与其对应的星期几连一条容量为1的边。如一部电影只能在周一、周三拍,且需要在3周内拍完,则该电影须与前3周的每个星期一和星期三都连一条边。

          从源点到每部电影连一条容量为D的边。

          从每周的每一天到汇点连一条容量为1的边。

反思:

             YES & Yes  ←←

             一开始构图的时候,只区分周几,而不区分第几周,无限wa。。

             提交时编译器选C++wa了,完全一样的代码G++过了,以后还是交G++了。。(C++是VC,有被微软做过优化,与G++存在区别。)

 

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

#define DEBUG(x) cout<<#x<<":"<<x<<endl
#define SET(a,x) memset(a,x,sizeof(a))
#define RE(i,n) for(int i = 0;i < n;i ++)
#define _RE(i,n) for(int i = 1;i <= n;i ++)
#define _FOR(i,j,k) for(int i = j;i <= k;i ++)
const int Max=400,aWeek=7;
int maze[Max][Max];
int week[aWeek],sink;
int input(int n)
{
    SET(maze,0);

    int w,ret = 0,MAX=0;
    _RE(i,n)
    {
        _RE(j,aWeek)
        scanf("%d",&maze[i][n+j]);

        scanf("%d",&maze[0][i]);
        ret += maze[0][i];
        scanf("%d",&w);
        if(MAX<w) MAX = w;
        _RE(j,aWeek)if(maze[i][n + j])
        {
            RE(k,w)
            maze[i][n+j+aWeek*k] = 1;
        }
    }
    sink = n + MAX * aWeek + 1;
    _FOR(i,n + 1,sink - 1)maze[i][sink] = 1;


    return ret;
}

int dis[Max],pre[Max],cur[Max],gap[Max];
int isap(int s,int t,int n)
{
    int maxflow = 0,aug = -1,u;
    SET(dis,0);
    SET(cur,0);
    SET(gap,0);
    pre[u = s] = s;
    gap[0]=n;

    while(dis[s]<n)
    {
loop:
        _FOR(v,cur[u],n)if(maze[u][v]&&dis[u]==dis[v]+1)
        {
            if(aug==-1||maze[u][v]<aug)aug = maze[u][v];
            pre[v] = u;
            u = cur[u] = v;
            if(u==t)
            {
                maxflow += aug;
                for(u = pre[u]; v!=s; v = u,u = pre[u])
                {
                    maze[u][v] -= aug;
                    maze[v][u] += aug;
                }
                aug = -1;
            }
            goto loop;
        }
        int mindis=n-1;
        _FOR(v,0,n)if(maze[u][v]&&mindis>dis[v])
        {
            cur[u] = v;
            mindis = dis[v];
        }
        if(--gap[dis[u]]==0)
        {
            break;
        }
        gap[dis[u] = mindis+1]++;
        u = pre[u];
    }

    return maxflow;
}

int main()
{
//    freopen("in.txt","r",stdin);
    int t,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);

        if(input(n)==isap(0,sink,sink+1))
            puts("Yes");
        else
            puts("No");
    }

}

 

 

 

 

 

poj2112 Optimal Milking

题意:

          有K台机器和C头奶牛,他们都在一个特定的位置上。

          给出他们之间的直接相连的道路的距离(如果没有则为0)。

          要求一台机器最多只能为M头奶牛工作。

          在满足所有奶牛都能有机器的前提下,所有奶牛到达机器都有一个距离(并不一定是直达的距离),取其中最大的一个,求这个距离最小可以是多少。

做法:

            (首先用floyd求最短路)

            每次二分最大距离,假设当前得到mid,就只考虑floyd后距离不大于mid的边。

            对于不大于mid的边:都连一条容量为1的边。

            从源点到每台机器连一条容量为M的边。

            从每头奶牛到汇点连一条容量为1的边。

反思:

             对floyd理解不深。一开始floyd的三层循环写错了,把最外层的放在了最里层,无限wa。。

 

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

#define DEBUG(x) cout<<#x<<":"<<x<<endl
#define SET(a,x) memset(a,x,sizeof(a))
#define RE(i,n) for(int i = 0;i < n;i ++)
#define _RE(i,n) for(int i = 1;i <= n;i ++)
#define _FOR(i,j,k) for(int i = j;i <= k;i ++)
const int Max=300,INF=1e9;

int k,c,m,n;
int s,t;
int maze[Max][Max],cap[Max][Max];

void input() {
    SET(cap,0);
    _RE(i,n)
    _RE(j,n) {
        scanf("%d",&maze[i][j]);
    }
}

int floyd() {

    int MAX=0;
    _RE(k,n)
    _RE(i,n)
    _RE(j,n)
     {
        if((maze[i][k]&&maze[k][j])&&(!maze[i][j]||maze[i][k]+maze[k][j]<maze[i][j]))
            maze[i][j]=maze[i][k]+maze[k][j];
        if(maze[i][j]>MAX)
            MAX=maze[i][j];
    }
    return MAX;
}

void SetCap(int dis) {
    SET(cap,0);

    _FOR(i,1,k)
    _FOR(j,k+1,n)      //!第二层循环是指奶牛,应从K+1开始
    if(maze[i][j]&&maze[i][j]<=dis)
        cap[i][j]=1;

    _RE(i,k)
    cap[s][i]=m;

    _FOR(i,k+1,n)
    cap[i][t]=1;
}

int dis[Max],pre[Max],cur[Max],gap[Max];
int isap(int s,int t,int n) {
    int maxflow = 0,aug = -1,u;
    SET(dis,0);
    SET(cur,0);
    SET(gap,0);
    pre[u = s] = s;
    gap[0]=n;

    while(dis[s]<n) {
loop:
        _FOR(v,cur[u],n)if(cap[u][v]&&dis[u]==dis[v]+1) {
            if(aug==-1||cap[u][v]<aug)aug = cap[u][v];
            pre[v] = u;
            u = cur[u] = v;
            if(u==t) {
                maxflow += aug;
                for(u = pre[u]; v!=s; v = u,u = pre[u]) {
                    cap[u][v] -= aug;
                    cap[v][u] += aug;
                }
                aug = -1;
            }
            goto loop;
        }
        int mindis=n-1;
        _FOR(v,0,n)if(cap[u][v]&&mindis>dis[v]) {
            cur[u] = v;
            mindis = dis[v];
        }
        if(--gap[dis[u]]==0) {
            break;
        }
        gap[dis[u] = mindis+1]++;
        u = pre[u];
    }

    return maxflow;
}

int main() {
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);

    while(~scanf("%d%d%d",&k,&c,&m)) {
        n=k+c;
        input();
        floyd();
        s=0;
        t=k+c+1;
        int l=0,mid,r=n*200,ret=r;

        while(l<=r) {
            mid=l+r;
            mid>>=1;
            SetCap(mid);
            if(isap(s,t,t+1)==c)
                r=mid-1,ret=mid;
            else
                l=mid+1;
        }
        printf("%d\n",ret);
    }
}

 

 

 

poj2455 Secret Milking Machine

题意:

         N个地点被P条路所连接,要从地点1到达地点N共T次。

         要让该过程走过的最长的路尽可能的小。

做法:

         每次二分最大距离,设当前为mid,就只考虑不大于mid的边。

         对于不大于mid的边都连一条容量为1的边。

         从1到N求最大流,看是否不小于T。

反思:

         邻接表的上限至少要是边上限的2倍,re了N次。。

         建边的时候其反向边是0,不是1。

         不该用引用的地方用了引用,对模板理解不深。

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define SET(a,x) memset(a,x,sizeof(a))
#define RE(i,N) for(int i = 0; i < N; i++)
#define _RE(i,N) for(int i = 1; i <= N; i ++)

const int MAXV = 220,MAXE = 40040 << 1;
int NV,P,T;
struct edgeSave {
    int a,b,l;
} save[MAXE];

int head[MAXV],ecnt;
struct edge {
    int v,w,next;
} e[MAXE];

bool cmp(edgeSave a,edgeSave b) {
    return a.l < b.l;
}

inline void insert(int u,int v,int w) {
    e[ecnt].v=v;
    e[ecnt].w=w;
    e[ecnt].next=head[u];
    head[u]=ecnt++;
}

void build(int m) {
    ecnt=0;
    SET(head,-1);
    RE(i,P) if(save[i].l <= m) {
        insert(save[i].a,save[i].b,1);
        insert(save[i].b,save[i].a,1);
    } else {
        break;
    }
}

int cur[MAXV] ,dis[MAXV] ,pre[MAXV] ,gap[MAXV];
int ISap(int s ,int t ,int NV) {
    int maxflow = 0, aug = -1, u;
    _RE(i,NV)cur[i] = head[i];
    SET(dis,0);
    SET(gap,0);
    gap[0] = NV;
    u = pre[s] = s;

    while (dis[s] < NV) {
loop:
        for(int &i = cur[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(e[i].w && dis[u] == dis[v] + 1) {
                if(aug == -1 || e[i].w < aug) aug = e[i].w;
                pre[v] = u;
                u = v;
                if(u == t) {
                    maxflow += aug;
                    for(u = pre[u]; v != s; v = u, u = pre[u]) {
                        e[cur[u]].w -= aug;
                        e[cur[u]^1].w +=aug;
                    }
                    aug = -1;
                }
                goto loop;
            }
        }

        int mindis = NV - 1;
        // !此处不能用引用类型,为此wa了数次,对模板理解不深
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(e[i].w && dis[v] < mindis) {
                cur[u] = i;
                mindis = dis[v];
            }
        }
        if(--gap[dis[u]] == 0)break;
        gap[ dis[u] = mindis + 1 ] ++;
        u = pre[u];
    }

    return maxflow;
}


int main() {
    while(~scanf("%d%d%d",&NV,&P,&T)) {

        RE(i ,P) {
            scanf("%d%d%d",&save[i].a,&save[i].b,&save[i].l);
        }
        sort(save, save + P, cmp);

        int l,m,r;
        l = 0;
        r = 1e6 + 6;
        while(l <= r) {
            m = (l + r) >> 1;
            build(m);
            int tmp = ISap(1,NV,NV);
            if(tmp >= T)
                r = m - 1;
            else
                l = m + 1;
        }
        cout<<l<<endl;
    }
}

 

 

 

 

 

poj3189 Steady Cow Assignment

题意:

         有N头奶牛和B个barns。

         每个barns都有一个容量。

         给出每头奶牛在选择barns时的志愿顺序。

        所有奶牛都能分配到barns,设第i头奶牛分配到的barns为其第x[i]志愿,

        求max{x[i],1<=i<=B,1<=x[i]<=B}与min{x[i]}之差最小可以是多少。答案是该差加1。

做法:

          首先枚举差为0的,即看能否找到一种分配,使得所有的x[i]都等于x。

          能的话答案就为1,不能的话就枚举长度为2的。即看能否找到一种分配使得max{x[i]}-min{x[i]}=2

          。。。。。。

          如果当前枚举差为L的,则可能的分配为每头奶牛 从第1志愿到第L+1志愿都考虑,

                                                                                       从第2志愿到的L+2志愿

                                                                                         。。。

                                                                                       从B-L+1到第B志愿

           如果都不能找到满足的分配,则L++,继续找。

反思:

          一开始理解错题意,以为是让max{x[i]}最小,看懂题意很重要。

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

#define RE(i,n) for(int i = 0; i < n; i++)
#define _RE(i,n) for(int i = 1; i <= n; i++)
const int MAXN = 1100, MAXB = 30;
int choice[MAXN][MAXB],cap[MAXB];
int N,B;

struct edge {
    int v,w,next;
} e[MAXN *MAXB *2];
int ecnt,head[MAXN];

void insert(int u,int v,int w) {
    e[ecnt].v = v;
    e[ecnt].w = w;
    e[ecnt].next = head[u];
    head[u] = ecnt++;

    e[ecnt].v = u;
    e[ecnt].w = 0;
    e[ecnt].next = head[v];
    head[v] = ecnt++;
}


void build(int begin,int len) {
    ecnt = 0;
    memset(head,-1,sizeof(head));

    int s = 0;
    _RE(i,N)insert(s,i,1);

    int end = begin + len;
    _RE(i,N)
    for(int j = begin; j <= end; j++)
    insert(i,choice[i][j],1);

    int t = N + B + 1;
    _RE(j,B)
    insert(j + N,t,cap[j]);

}

int dis[MAXN], pre[MAXN], gap[MAXN], cur[MAXN];
int isap(int s,int t,int NV) {

    int maxflow = 0, aug = -1, u;
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    for(int i = 0; i <= NV; i++) cur[i] = head[i];
    u = pre[s] = s;
    gap[0] = NV;

    while(dis[s] < NV) {
loop:
        for(int &i = cur[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(e[i].w && dis[u] == dis[v] + 1) {
                if(aug == -1 || e[i].w <aug) aug = e[i].w;
                pre[v] = u;
                u = v;
                if(u == t) {
                    maxflow += aug;
                    for(u = pre[u]; v != s; v = u, u = pre[u]) {
                        e[cur[u]].w -= aug;
                        e[cur[u]^1].w += aug;
                    }
                    aug = -1;
                }
                goto loop;
            }
        }
        int mindis = NV;
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(e[i].w && dis[v] < mindis) {
                mindis = dis[v];
                cur[u] = i;
            }
        }
        if((--gap[dis[u]]) == 0) break;
        dis[u] = mindis + 1;
        gap[dis[u]]++;
        u = pre[u];
    }
    return maxflow;
}

int main() {
//    freopen("in.txt","r",stdin);
    while(~scanf("%d%d",&N,&B)) {
        _RE(i,N)
        _RE(j,B) {
            scanf("%d",&choice[i][j]);
            choice[i][j] += N;
        }

        _RE(i,B)
        scanf("%d",cap + i);

        int ret = 0, s = 0, t = N + B + 1;

        bool flag = 1;
        for(int i = 0; i <B && flag; i++)//higest - lowest : i
        for(int j = 1, end = B - i; j <= end && flag; j++){
            ret = i + 1;
            build(j,i);
            if(isap(s,t,t+1) >= N)
                flag = 0;
        }
        printf("%d\n",ret);
    }
    return 0;
}

 

 

 

poj1637 Sightseeing tour

题意:

         混合图求是否存在欧拉回路。

做法:

          参考google到的做法写的。下面是自己的理解。

          由于无向边只需走一遍,在确定其走向后可当做有向边。因此我们可以先任意给定它一个方向,使之当做有向边。然后就成了一幅有向图。

          对于有向图,存在欧拉回路的充要条件是每个点的出度等于入度。但由于有一些无向边被我们任意确定了一个方向,所以必要条件(不是充要。如只考虑有向边的时候,A顶点的入度为10,出度为2;A点还有6条无向边与之相连,无论这6条无向边被我们当成入边还是出边,最后都能使入度出度之差为偶数;但即使这6条边都当做出边,也不能使A点出入度相等。)变成每个点的入度和出度之差为偶数。

        在把所有无向边任意定向,并经过上面的预判之后,就可以用网络流进一步确认是否存在欧拉回路。

        求最大流时,所有有向边都不连,无向边就按之前任意确定的方向当做有向边连接。

         设x[i]为每个点入度与出度之差。若x[i]>0,则从该点到汇点连一条容量为x[i]/2的边。

                                                           若x[i]<0,则从源点到该点连一条容量为-x[i]/2的边。

 

反思:

         边的上限又开小了。。ORz

 

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;

const int maxm = 220, maxs = 1010 * 2 + maxm * 2;
int head[maxm],in[maxm],out[maxm];
struct edge {
    int v,w,next;
} e[maxs];
int ecnt;

void insert(int u,int v,int w,int ww = 0) {
    e[ecnt].v = v;
    e[ecnt].w = w;
    e[ecnt].next = head[u];
    head[u] = ecnt++;

    e[ecnt].v = u;
    e[ecnt].w = ww;
    e[ecnt].next = head[v];
    head[v] = ecnt++;
}

int cur[maxm],pre[maxm],gap[maxm],dis[maxm];
int isap(int s,int t,int n) {
    int maxflow = 0,aug = -1,u;
    memset(gap,0,sizeof(gap));
    memset(dis,0,sizeof(dis));
    gap[0] = n;
    for(int i = 0; i < n; i++)
        cur[i] = head[i];
    pre[u = s] = s;

    while(dis[s] < n) {
loop:
        for(int &i = cur[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(e[i].w && dis[u] == dis[v] + 1)  {
                if(aug == -1 || e[i].w < aug)
                    aug = e[i].w;
                pre[v] = u;
                u = v;
                if(u == t) {
                    maxflow += aug;
                    for(u = pre[u]; v != s; v = u, u = pre[u]) {
                        e[cur[u]].w -= aug;
                        e[cur[u] ^ 1].w +=aug;
                    }
                    aug = -1;
                }
                goto loop;
            }
        }

        int mindis = n;
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(e[i].w && dis[v] < mindis) {
                mindis = dis[v];
                cur[u] = i;
            }
        }

        if(--gap[dis[u]] == 0) break;
        gap[dis[u] = mindis + 1]++;
        u = pre[u];

    }

    return maxflow;
}



int main() {
//    freopen("data.txt","r",stdin);
    int n;
    int m,s;
    scanf("%d",&n);
    while(n--) {
        scanf("%d%d",&m,&s);
        memset(head,-1,sizeof(head));
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
      
        ecnt = 0;

        int x,y,d;
        while(s--) {
            scanf("%d%d%d",&x,&y,&d);
            out[x]++;in[y]++;
            if(d == 0) {
                insert(x,y,1);
   
            }
        }

        bool flag = true;
        int sum = 0,s = 0, t = m + 1;
        for(int i = 1; i <= m; i++) {
        
            x = in[i] - out[i];
            y = x > 0 ? x : -x;
            if(y & 1) {
                flag = false;
                break;
            }
            if(x > 0) {
                insert(i,t,x / 2);
                sum += x/2;
            } else if(x < 0)
                insert(s,i,-x / 2);
        }

        if(!flag || ( isap(s,t,m + 2)) != sum)
            puts("impossible");
        else
            puts("possible");

    }
}

 

 

 

poj3498 March of the Penguins

题意:

          给出N块浮冰的坐标、上面的企鹅数量、在消失前最多能有多少只企鹅离开(每次有企鹅离开,浮冰都会更接近消失)。

          问所有的企鹅可以在哪些浮冰上会面。

做法:

          对于每块浮冰,既有企鹅数量,又有消失次数。只从源点往这些点连一条边的话,只能表示其中一个。这样只控制了该点的入边,出边由于有很多,看似不能控制。

          但是,我们可以把每个点分成两个点,如点1映射成点2、点3,从源点到点2表示企鹅数量,从点2到点3表示消失次数即可。也就是说把点2和点3看成一个整体,点2只有入边,点3只有出边。然后再把所有距离不大于D的边都连一条容量为sum(企鹅总数量)的边。

反思:

           一开始把sum写成了N,wa了一次,所幸是自己找出来的。。ORz

 

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>

using namespace std;

#define DEBUG(x) cout<<#x<<":"<<x<<endl;
#define SET(a,x) memset(a,x,sizeof(a))
#define RE(i,n) for(int i = 0; i < (n); i++)
#define _FOR(i,j,k) for(int i = (j); i <= (k); i++)
const int MAXN = 110,MAXV = MAXN << 1;

struct floes {
    int x,y,n,m;
} save[MAXN];
int mat[MAXN][MAXN];
int N,S,T,sum;
double D;

inline double dist(int i,int j) {
    return ((save[i].x - save[j].x) * (save[i].x - save[j].x) + (save[i].y - save[j].y) * (save[i].y - save[j].y) );
}

void calculate() {
    SET(mat,0);
    double DD = D * D;
    RE(i,N)
    RE(j,N)
    if(i != j && dist(i,j) <= DD)
        mat[i][j] = 1;
}

int map[MAXV][MAXV];
void setMap(int tar) {
    T = tar << 1;
    SET(map,0);
    RE(i,N)
    map[i << 1][i << 1 | 1] = save[i].m;
    map[T][T | 1] = 0;
    RE(i,N)
    RE(j,N)if(mat[i][j]) {
        map[i << 1 | 1][j << 1] =sum;//!!
    }
    S = N << 1;
    RE(i,N)
    map[S][i << 1] = save[i].n;
    map[S][T] = 0;

}

int cur[MAXV],dis[MAXV],pre[MAXV],gap[MAXV];
int isap(int s,int t,int n) {

    int maxflow = 0,u,aug = -1;
    SET(cur,0);
    SET(dis,0);
    SET(gap,0);

    pre[u = s] = s;
    gap[0] = n;

    while(dis[s] < n) {
loop:
        _FOR(v,cur[u],n)if(map[u][v] && dis[u] == dis[v] + 1) {
            if(aug == -1 || map[u][v] < aug)
                aug = map[u][v];

            pre[v] = u;
            u = cur[u] = v;

            if(u == t) {
                maxflow += aug;
                for(u = pre[u]; v != s; v = u, u =pre[u]) {
                    map[u][v] -= aug;
                    map[v][u] += aug;
                }
                aug = -1;
            }
            goto loop;
        }

        int mindis = n;
        _FOR(v,0,n)if(map[u][v] && dis[v] < mindis) {
            mindis = dis[v];
            cur[u] = v;
        }
        if(--gap[dis[u]] == 0)break;
        gap[dis[u] =mindis + 1]++;
        u = pre[u];
    }
    return maxflow;
}

int main() {
//    freopen("in.txt","r",stdin);

    int t;
    for(scanf("%d",&t); t--;) {
        sum = 0;
        scanf("%d%lf",&N,&D);
        RE(i,N) {
            scanf("%d%d%d%d",&save[i].x,&save[i].y,&save[i].n,&save[i].m);
            sum += save[i].n;
        }
        calculate();
        bool first = true;
        RE(i,N) {
            setMap(i);
            if(sum -save[i].n == isap(S,T,S | 1)) {
                if(first)printf("%d",i),first = false;
                else printf(" %d",i);
            }
        }
        if(first)
            puts("-1");
        else
            puts("");
    }
    return 0;
}

 

 

 

 

 

 

最小割:

hdu3987  Harry Potter and the Forbidden Forest 

题意:

         混合图,破坏一些边后使点0和点n-1不相通,要求破坏的边权之和最小,求这些边的条数。

做法:

         首先,根据最小割最大流定理,求最大流后那些满流的边就是要破坏掉的。

         1.把每条边的权W变成W*E+1(E为大于边数的正整数),然后求一次最大流maxflow。答案即为maxflow%E。

         这是因为不满流的边 所通过的流量必然为E的倍数。只有满流的边 所通过的流量%E=1。

         2.求一遍最大流。把所有满流的边权值赋为1,非满流的赋为INF。再求一遍最大流即为答案。

 

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>

using namespace std;

#define RE(i,n) for(int i = 0; i < (n); i++)
typedef long long ll;
const int N = 1000 + 10;
const int M = (100000 << 2) + 10;
struct edge {
    int v;
    ll w;
    int next;
} e[M];
int head[N],ecnt;
int n,m;

void init() {
    memset(head,-1,sizeof(head));
    ecnt = 0;
}

void addedge(int u,int v,ll w) {
    edge tmp = {v,w,head[u]};
    e[ecnt] = tmp;
    head[u] = ecnt++;

    edge tmp2 = {u,0,head[v]};
    e[ecnt] = tmp2;
    head[v] = ecnt++;

}

void input() {

    scanf("%d%d",&n,&m);
    int u,v;
    ll w,d;

    RE(i,m) {
        scanf("%d%d%I64d%I64d",&u,&v,&w,&d);

        w = w * M + 1;
        addedge(u,v,w);
        if(d)
            addedge(v,u,w);
    }
}

int dis[N],pre[N],gap[N],cur[N];
ll isap(int s,int t,int n) {
    ll maxflow = 0;
    RE(i,n)
    cur[i] = head[i];
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    gap[0] = n;

    int u;
    ll aug = -1;
    pre[s] = u = s;

    while(dis[s] < n) {
loop:
        for(int& i = cur[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(e[i].w && dis[u] == dis[v] + 1) {
                if(aug == -1 || e[i].w < aug)
                    aug = e[i].w;
                pre[v] = u;
                u = v;
                if(u == t) {
                    maxflow += aug;
                    for(u = pre[u]; v != s; v = u, u = pre[u]) {
                        e[cur[u]].w -= aug;
                        e[cur[u] ^ 1].w += aug;
                    }
                    aug = -1;
                }
                goto loop;
            }
        }

        int mindis = n;
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(e[i].w && dis[v] < mindis) {
                mindis = dis[v];
                cur[u] = i;
            }
        }

        if(-- gap[dis[u]] == 0)
            break;
        gap[dis[u] = mindis + 1] ++;
        u = pre[u];
    }

    return maxflow;
}

int main() {
    int t,cas = 0;
    scanf("%d",&t);
    while(t--) {
        init();
        input();
        printf("Case %d: %I64d\n",++cas,isap(0,n-1,n) % M );
    }
    return 0;
}

 

 

 

 

 

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>

using namespace std;

#define RE(i,n) for(int i = 0; i < (n); i++)
typedef long long ll;
const int N = 1000 + 10;
const int M = (100000 << 2) + 10;
struct edge {
    int v;
    ll w,c;
    int next;
} e[M];
int head[N],ecnt;
int n,m;

void init() {
    memset(head,-1,sizeof(head));
    ecnt = 0;
}

void addedge(int u,int v,ll w) {
    edge tmp = {v,w,w,head[u]};
    e[ecnt] = tmp;
    head[u] = ecnt++;

    edge tmp2 = {u,0,0,head[v]};
    e[ecnt] = tmp2;
    head[v] = ecnt++;

}

void input() {

    scanf("%d%d",&n,&m);
    int u,v;
    ll w,d;

    RE(i,m) {
        scanf("%d%d%I64d%I64d",&u,&v,&w,&d);

//        w = w * M + 1;
        addedge(u,v,w);
        if(d)
            addedge(v,u,w);
    }
}

int dis[N],pre[N],gap[N],cur[N];
ll isap(int s,int t,int n) {
    ll maxflow = 0;
    RE(i,n)
    cur[i] = head[i];
    memset(dis,0,sizeof(dis));
    memset(gap,0,sizeof(gap));
    gap[0] = n;

    int u;
    ll aug = -1;
    pre[s] = u = s;

    while(dis[s] < n) {
loop:
        for(int& i = cur[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(e[i].w && dis[u] == dis[v] + 1) {
                if(aug == -1 || e[i].w < aug)
                    aug = e[i].w;
                pre[v] = u;
                u = v;
                if(u == t) {
                    maxflow += aug;
                    for(u = pre[u]; v != s; v = u, u = pre[u]) {
                        e[cur[u]].w -= aug;
                        e[cur[u] ^ 1].w += aug;
                    }
                    aug = -1;
                }
                goto loop;
            }
        }

        int mindis = n;
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(e[i].w && dis[v] < mindis) {
                mindis = dis[v];
                cur[u] = i;
            }
        }

        if(-- gap[dis[u]] == 0)
            break;
        gap[dis[u] = mindis + 1] ++;
        u = pre[u];
    }

    return maxflow;
}

const int INF = 8e8;
void rebuild() {
    RE(i,ecnt) {
        if(i & 1)
            e[i].w = 0;
        else {
            if(e[i].w == 0)
            e[i].w = 1;
            else
            e[i].w = INF;
        }
    }
}

int main() {
    int t,cas = 0;
    scanf("%d",&t);
    while(t--) {
        init();
        input();
        isap(0,n-1,n);
        rebuild();
        printf("Case %d: %I64d\n",++cas,isap(0,n-1,n) % M );
    }
    return 0;
}

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值