BZOJ1001 [Beijing2006] 狼抓兔子

本文介绍如何利用最大流-最小割定理解决特定信息学竞赛问题,通过实例讲解了如何构造网络图并使用SPFA算法求解最大流。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

相关资料:

浅析最大最小定理在信息学竞赛中的应用 北京大学ACM暑期课讲义-网络流

根据最大流-最小割定理,一个网络中,两个边缘点之间的最大流等于最小割(最小割就是用一条割线将两个点分割在两个图中,令删去的边的总权值最小。因此我们可以建图,将每个图(在本题中是每个三角形)作为一个点,点与点之间的连线就等于它们穿过的那条线的权值。在起点和终点间连一条线,线的内部和外部分别建立两个点,将这两个点和相邻的三角形连线,然后删去这条边在整个图中求最短路即可。

代码: (ps:当时也是在学习的过程中,所以借鉴了别人的代码)


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

using namespace std;

const int N=2000006, INF=0x3fffffff, E=N*3;

struct ARC {
    int u, val, next;
    inline void init(int a, int b, int c) {
        u=a, val=b, next=c;
    }
} arc[E];
int head[N], tot, S, T, n, m, dis[N];
bool vs[N];

inline void add_arc(int s, int t, int val) {
    arc[tot].init(t, val, head[s]);
    head[s]=tot++;
}

void SPFA(){
     queue<int> q;
     fill(dis, dis+T+1, INF);
     fill(vs, vs+T+1, 0);
     q.push(S);
     dis[S]=S,vs[S]=true;
     while (!q.empty()){
           int c=q.front(),i;
           q.pop();vs[c]=false;
           for (i=head[c]; i!=-1; i=arc[i].next){
               if (dis[c]+arc[i].val<dis[arc[i].u]){
                  dis[arc[i].u]=dis[c]+arc[i].val;
                  if (!vs[arc[i].u]){
                     q.push(arc[i].u);
                     vs[arc[i].u]=true;
                  }
               }
           }
     }
     printf("%d",dis[T]);
}

void read(int &x) {
    char c;
    while((c=getchar())<'0' || c>'9');
    x=c-'0';
    while((c=getchar())>='0' && c<='9') x=(x<<3)+(x<<1)+c-'0';
}

void Input() {//读入
    for(int i=0, id1, id2, a; i<=n-1; i++)//读入相邻横边
        for(int j=1; j<=m-1; j++) {
            read(a);
            id1=((i-1)*(m-1)+j)*2-1;
            id2=(i*(m-1)+j)*2;//从1图通往2图
            if(i==0) id1=T,add_arc(id2, id1, a);
            else if(i==n-1) id2=S,add_arc(id2, id1, a);
            else add_arc(id1, id2, a),add_arc(id2, id1, a);
        }//增边

    for(int i=1, id1, id2, a; i<=n-1; i++)
        for(int j=0; j<m; j++) {
            read(a);
            id1=((i-1)*(m-1)+j)*2;
            id2=((i-1)*(m-1)+j+1)*2-1;
            if(j==0) id1=S,add_arc(id1, id2, a);
            else if(j==m-1) id2=T,add_arc(id1, id2, a);
            else add_arc(id1, id2, a),add_arc(id2, id1, a);
        }

    for(int i=1, id1, id2, a; i<=n-1; i++)
        for(int j=1; j<=m-1; j++) {
            read(a);
            id1=((i-1)*(m-1)+j)*2;
            id2=((i-1)*(m-1)+j)*2-1;
            add_arc(id1, id2, a);
            add_arc(id2, id1, a);
        }
}

int main() {
    freopen("1001.in","r",stdin);
    freopen("1001.out","w",stdout);
    read(n), read(m);//读入整数
    S=0, T=(n-1)*(m-1)*2+1;//S是源点T是汇点
    fill(head, head+T+1, -1), tot=0;//初始化为全-1
    if(n==1 || m==1) {//处理特殊情况
        if(n>m) swap(n, m);
        int ans=INF;
        for(int i=1, a; i<m; i++) {
            read(a);
            if(ans>a) ans=a;
        }
        printf("%d\n", ans==INF?0:ans);
    }
    else Input(), SPFA();
    return 0;
}

//BZOJ 1001

ps谁告诉我为什么代码片点确定没反应qwqwq

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值