有上下界的网络流小结

1. 无源汇可行流
对于有下界的网络流,首先要想办法去掉下界,做法是把原图中的流量分成流b和f’,其中b的流量为原流量下界, f=fb
虚拟一个源点s和一个汇点t,对每个点u,设 D(u)=binbout ,若 D(u)>0 ,从s连一条容量为D(u)的流到u,否则连一条容量为-D(u)的流到t。其实这个做法相当于对于每一个流入u的下界,连一条s到u的流,对于每一条流出u的下界,连一条u到t的流,这样的话原图中的流量下界都由s和t来提供,原图中的流流量变为 fb ,容易发现s流出的流和t流入的流流量总和一定相等,如果原图满足下界限制的话,s和t的流都应该是满流的,这时候在原图中产生的流量可以认为是为了满足下界而做出的流量调整。
建完图,跑一遍最大流,如果s->t满流则说明有可行流,一条可行流的边上流量为新图中流的流量+它的下界。

例题:zoj2314

#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int n, m, tot,s,t;
int head[205], cur[205], h[205], q[205], in[205];
int l[100005];
struct edge { int to, next, cap; }e[100005];
void init() {
    tot = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int w) {
    e[tot].to = v; e[tot].next = head[u]; e[tot].cap = w; head[u] = tot++; 
    e[tot].to = u; e[tot].next = head[v]; e[tot].cap = 0; head[v] = tot++;
}
bool bfs() {
    memset(h,-1,sizeof(h));
    int tmp = 0, w = 1; q[s] = 0; h[s] = 0;
    while (tmp != w) {
        int now = q[tmp]; tmp++;
        for (int i = head[now]; i!=-1; i = e[i].next)
            if (e[i].cap&&h[e[i].to] == -1) {
                h[e[i].to] = h[now] + 1;
                q[w++] = e[i].to;
            }
    }
    if (h[t] == -1)return 0;
    return 1;
}
int dfs(int x, int f) {
    if (x == t)return f;
    int w, used = 0;
    for (int i = cur[x]; i!=-1; i = e[i].next)
        if (h[e[i].to] == h[x] + 1) {
            w = f - used; w = dfs(e[i].to, min(e[i].cap, w));
            e[i].cap -= w; if (e[i].cap)cur[x] = i; e[i ^ 1].cap += w;
            used += w; if (used == f)return f;
        }
    if (!used)h[x] = 1;
    return used;
}
int dinic() {
    int ret = 0;
    while (bfs()) { 
        for (int i = 0; i <= t; i++)
            cur[i] = head[i]; 
        ret += dfs(s, inf); 
    }
    return ret;
}
int main() {
    int T, u, v, c;
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d", &n, &m);
        s = 0,t = n + 1;
        init();
        memset(in,0,sizeof(in));
        for (int i = 0; i<m; i++){
            scanf("%d%d%d%d", &u, &v, l + i, &c);
            addedge(u, v, c - l[i]);
            in[u] -= l[i];
            in[v] += l[i];
        }
        int sum = 0;
        for(int i = 1;i<=n;i++){
            if(in[i] > 0){
                addedge(s,i,in[i]);
                sum += in[i];
            }
            else addedge(i,t,-in[i]);
        }
        if(sum != dinic()){
            printf("NO\n\n");
            continue;
        }
        printf("YES\n");
        for(int i = 0;i<m;i++){
            printf("%d\n",e[i<<1|1].cap + l[i]);
        }
        printf("\n");
    }
}

2. 有源汇可行流
有源汇可行流的情况是在上一种情况的基础上指定了源点和汇点。解决这种问题的方法是转换为无源的情况(和下面两个问题的解决方法一样)。在原图中连一条t到s,容量为INF的流。虚拟出一个超级源点ss和超级汇点tt,按上个问题的方式建图。这里连一条t到s的流的意义在于完成原图中下界约束的传递关系,这是我的理解…这样的话ss和tt间流的传递就成为了一个整体…实际上t->s的流的流量即可行流s->t的流量。(好像说了句废话,不过这个性质在后面会被用到)

例题:zoj2396

int main(){
//  freopen("a.in","r",stdin);
    int T;
    scanf("%d",&T);
    int c,x,y,z;
    char str[3];
    while(T--){
        scanf("%d%d",&m,&n);
        init();
        s = n + m + 1;
        t = s + 1;
        for(int i = 1;i<=m;i++){
            scanf("%d",&c);
            // c/c
            addedge(s,i,0);
            in[s] -= c;
            in[i] += c;
        }
        for(int i = 1;i<=n;i++){
            scanf("%d",&c);
            addedge(m+i,t,0);
            in[m+i] -= c;
            in[t] += c;
        }
        scanf("%d",&c);
        while(c--){
            scanf("%d%d%s%d",&x,&y,str,&z);
            int i1,i2,j1,j2;
            if(x == 0){
                i1 = 1;i2 = m;
            }else
                i1 = i2 =x;
            if(y == 0){
                j1 = 1;j2 = n;
            }else
                j1 = j2 = y;
            for(int i = i1;i<=i2;i++){
                for(int j = j1;j<=j2;j++){
                    if(str[0] == '>'){
                        lim[0][i][j] = max(lim[0][i][j],z+1);
                    }else if(str[0] == '<'){
                        lim[1][i][j] = min(lim[1][i][j],z-1);
                    }else{
                        lim[0][i][j] = max(lim[0][i][j],z);
                        lim[1][i][j] = min(lim[1][i][j],z);
                    }
                }
            }

        }
        bool flag = 0;
        for(int i = 1;i<=m;i++){
            for(int j = 1;j<=n;j++){
                if(lim[0][i][j] > lim[1][i][j]){                        
                    flag = 1;
                    break;
                }
                addedge(i,j+m,lim[1][i][j] - lim[0][i][j]);
                id[i][j] = tot-1;
                in[i] -= lim[0][i][j];
                in[m+j] += lim[0][i][j];
            }
            if(flag) break;
        }
        if(flag){
            printf("IMPOSSIBLE\n");
            continue;
        }
        addedge(t,s,inf);
        int num = t;
        s += 2;t += 2;
        int sum = 0;
        for(int i  = 1;i<=num;i++){
            if(in[i] > 0){
                addedge(s,i,in[i]);
                sum += in[i];
            }else
                addedge(i,t,-in[i]);
        }
        if(dinic() != sum){
            printf("IMPOSSIBLE\n\n");
            continue;
        }
        for(int i = 1;i<=m;i++){
            for(int j = 1;j<=n;j++) {
                printf("%d%c",lim[0][i][j]+e[id[i][j]].cap,j == n?'\n':' ');
            }
        }
    }
}

3. 有源汇最大流

因为跑一次最大流跑出来的只是一个可行解,而非原图中s->t的最大流,所以去掉ss和tt,在上一次最大流跑完后的残留网络上再跑一次最大流,结果就是所要的答案,这是因为残留网络上t->s的流量被退回来,相当于是加上了可行流的流量。

例题:zoj3229

int main(){
    //freopen("a.in","r",stdin);
    int g,c,d,l,r;
    while(~scanf("%d%d",&n,&m)){
        init();
        s = n + m;
        t = s + 1;
        for(int i = 0;i<m;i++){
            scanf("%d",&g);
            addedge(i+n,t,inf-g);
            in[i+n] -= g;
            in[t] += g;
        }
        for(int i = 0;i<n;i++){
            scanf("%d%d",&c,&d);
            addedge(s,i,d);
            for(int j  = 0;j<c;j++){
                scanf("%d%d%d",&g,&l,&r);
                addedge(i,g+n,r-l);
                in[i] -= l;
                in[g+n] += l;
                low[i][g] = l;
                id[i][g] = tot - 1;
            }
        }
        addedge(t,s,inf);
        int num = t;
        s += 2;t += 2;
        int sum = 0;
        for(int i  = 0;i<=num;i++){
            if(in[i] > 0){
                addedge(s,i,in[i]);
                sum += in[i];
            }else
                addedge(i,t,-in[i]);
        }
        if(dinic() != sum){
            printf("-1\n\n");
            continue;
        }
        head[s] = head[t] = -1;
        s -= 2;t -= 2;
        printf("%d\n",dinic());
        //之所以只需要再跑一次最大流,是因为第一次的流量储存在t->s中,第二次最大流会加上这一段的流量
        for(int i = 0;i<n;i++){
            for(int j = 0;j<m;j++) {
                if(id[i][j]){
                    printf("%d\n",low[i][j] + e[id[i][j]].cap);
                }
            }
        }
        printf("\n");
    }
}

4. 有源汇最小流
有人会认为使用可行流的解法跑出来的结果就是最小流,实际上并不是这样,这是因为在原图中可能存在有圈,这样的话可能不需要通过增加s->t的流量来满足圈中边的下界,因为圈中可以有环流。
求最小流一个比较巧妙的做法是增设ss和tt,但不连t->s的流,先跑一次最大流,这样可以去除上述的冗余流量(我的理解)。再连一条t->s的流,跑一次ss->tt的最大流,这时候s->t的流量(也是t->s的流的流量)即为最小流。

例题:poj3801

int main(){
    //freopen("a.in","r",stdin);
    char s1[5],s2[5];
    int a,b,c;
    while(~scanf("%d%d",&n,&m)){
        if(n + m == 0) break;
        init();
        s = n + 1;
        t = s + 1;
        for(int i = 0;i<m;i++){
            scanf("%s%s%d",s1,s2,&c);
            a = getnum(s1);
            b = getnum(s2);
            addedge(a,b,inf-c);
            in[a] -= c;
            in[b] += c;
        }
        int num = t;
        s += 2;t += 2;
        int sum = 0;
        for(int i  = 0;i<=num;i++){
            if(in[i] > 0){
                addedge(s,i,in[i]);
                sum += in[i];
            }else
                addedge(i,t,-in[i]);
        }
        addedge(t-2,s-2,inf);
        if(dinic() != sum){
            printf("impossible\n");
            continue;
        }
        printf("%d\n",e[tot-1].cap);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 JavaScript 编写的记忆游戏(附源代码)   项目:JavaScript 记忆游戏(附源代码) 记忆检查游戏是一个使用 HTML5、CSS 和 JavaScript 开发的简单项目。这个游戏是关于测试你的短期 记忆技能。玩这个游戏 时,一系列图像会出现在一个盒子形状的区域中 。玩家必须找到两个相同的图像并单击它们以使它们消失。 如何运行游戏? 记忆游戏项目仅包含 HTML、CSS 和 JavaScript。谈到此游戏的功能,用户必须单击两个相同的图像才能使它们消失。 点击卡片或按下键盘键,通过 2 乘 2 旋转来重建鸟儿对,并发现隐藏在下面的图像! 如果翻开的牌面相同(一对),您就赢了,并且该对牌将从游戏中消失! 否则,卡片会自动翻面朝下,您需要重新尝试! 该游戏包含大量的 javascript 以确保游戏正常运行。 如何运行该项目? 要运行此游戏,您不需要任何类型的本地服务器,但需要浏览器。我们建议您使用现代浏览器,如 Google Chrome 和 Mozilla Firefox, 以获得更好、更优化的游戏体验。要玩游戏,首先,通过单击 memorygame-index.html 文件在浏览器中打开游戏。 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值