zoukankan      html  css  js  c++  java
  • 【wikioi】1913 数字梯形问题(费用流)

    http://wikioi.com/problem/1913/

    如果本题没有询问2和3,那么本题和蚯蚓那题一模一样。http://www.cnblogs.com/iwtwiioi/p/3935039.html

    我们来分析询问2和3。

    首先,询问2允许重复经过点。我们想想询问1的做法,是拆点,为什么?因为要控制只走一次。so,询问2就将拆的点去掉就行了。重构一次图,将上边的点向下连边,容量为1,费用为下边那个点的权值。然后现在还有第一行的点没有构造,那么将源连边到第一行的点,和之前相同,容量为1,费用为这些点的权值。接下来我们连最后一行到汇,因为可以允许重复点,那么就将容量设为oo,费用为0。(为什么这样不会走重复边呢?容量为1当然不会重复走。。)

    然后我们来看询问3,只是在2的基础上加了个允许边重复。哈哈,重复边还不简单,将中间连的那些边容量全部设为oo,只不过在源连的边容量要为1,因为要限制次数。

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <string>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define rep(i, n) for(int i=0; i<(n); ++i)
    #define for1(i,a,n) for(int i=(a);i<=(n);++i)
    #define for2(i,a,n) for(int i=(a);i<(n);++i)
    #define for3(i,a,n) for(int i=(a);i>=(n);--i)
    #define for4(i,a,n) for(int i=(a);i>(n);--i)
    #define CC(i,a) memset(i,a,sizeof(i))
    #define read(a) a=getint()
    #define print(a) printf("%d", a)
    #define dbg(x) cout << #x << " = " << x << endl
    #define printarr(a, n, m) rep(aaa, n) { rep(bbb, m) cout << a[aaa][bbb]; cout << endl; }
    inline const int getint() { int r=0, k=1; char c=getchar(); for(; c<'0'||c>'9'; c=getchar()) if(c=='-') k=-1; for(; c>='0'&&c<='9'; c=getchar()) r=r*10+c-'0'; return k*r; }
    inline const int max(const int &a, const int &b) { return a>b?a:b; }
    inline const int min(const int &a, const int &b) { return a<b?a:b; }
    
    const int N=5500, M=2000000, oo=~0u>>1, s=0, t=5200, ss=t+1, tt=ss+1;
    int ihead[N], cnt=1, d[N], p[N], n, m, vis[N], q[N], front, tail, id[35][70], nd[35][70];
    struct ED { int from, to, cap, w, next; } e[M];
    inline void add(const int &u, const int &v, const int &c, const int &w) {
    	e[++cnt].next=ihead[u]; ihead[u]=cnt; e[cnt].to=v; e[cnt].from=u; e[cnt].cap=c; e[cnt].w=w;
    	e[++cnt].next=ihead[v]; ihead[v]=cnt; e[cnt].to=u; e[cnt].from=v; e[cnt].cap=0; e[cnt].w=-w;
    }
    inline const bool spfa(const int &s, const int &t) {
    	for1(i, 0, t) d[i]=1000000000, vis[i]=0;
    	vis[s]=1; d[s]=front=tail=0; q[tail++]=s;
    	int u, v, i;
    	while(front!=tail) {
    		u=q[front++]; if(front==N) front=0;
    		for(i=ihead[u]; i; i=e[i].next) if(e[i].cap && d[v=e[i].to]>d[u]+e[i].w) {
    			d[v]=d[u]+e[i].w; p[v]=i;
    			if(!vis[v]) {
    				vis[v]=1, q[tail++]=v;
    				if(tail==N) tail=0;
    			}
    		}
    		vis[u]=0;
    	}
    	return d[t]!=1000000000;
    }
    int mcf(const int &s, const int &t) {
    	int ret=0, f, u;
    	while(spfa(s, t)) {
    		for(f=oo, u=t; u!=s; u=e[p[u]].from) f=min(f, e[p[u]].cap);
    		for(u=t; u!=s; u=e[p[u]].from) e[p[u]].cap-=f, e[p[u]^1].cap+=f;
    		ret+=d[t]*f;
    	}
    	return ret;
    }
    void rebuild() {
    	CC(ihead, 0); cnt=1; int now;
    	for1(i, 1, n) for1(j, 1, m+i-1) {
    		now=id[i][j];
    		if(i<n) add(now, id[i+1][j], 1, -nd[i+1][j]), add(now, id[i+1][j+1], 1, -nd[i+1][j+1]);
    	}
    	for1(j, 1, m) add(s, id[1][j], 1, -nd[1][j]);
    	for1(j, 1, m+n-1) add(id[n][j], t, oo, 0);
    	add(ss, s, oo, 0); add(t, tt, oo, 0);
    }
    int main() {
    	read(m); read(n);
    	int c, now, tot=0, pw=(n+m-1)*(n+m-1);
    	for1(i, 1, n) for1(j, 1, m+i-1) id[i][j]=++tot;
    	for1(i, 1, n) for1(j, 1, m+i-1) {
    		read(c); now=id[i][j]; nd[i][j]=c;
    		add(now, now+pw, 1, -c);
    		if(i<n) add(now+pw, id[i+1][j], 1, 0), add(now+pw, id[i+1][j+1], 1, 0);
    	}
    	for1(j, 1, m) add(s, id[1][j], 1, 0);
    	for1(j, 1, m+n-1) add(id[n][j]+pw, t, 1, 0);
    	add(ss, s, m, 0); add(t, tt, m, 0);
    	printf("%d
    ", -mcf(ss, tt));
    
    	//case 2
    	rebuild();
    	printf("%d
    ", -mcf(ss, tt));
    
    	//case 3
    	for(int i=2, tot=cnt; i<=tot; i+=2) if(e[i].from!=s) e[i].cap=oo, e[i^1].cap=0; else e[i].cap=1, e[i^1].cap=0;
    	printf("%d
    ", -mcf(ss, tt));
    
    	return 0;
    }
    

    给定一个由n 行数字组成的数字梯形如下图所示。梯形的第一行有m 个数字。从梯形
    的顶部的m 个数字开始,在每个数字处可以沿左下或右下方向移动,形成一条从梯形的顶
    至底的路径。
    规则1:从梯形的顶至底的m条路径互不相交。
    规则2:从梯形的顶至底的m条路径仅在数字结点处相交。
    规则3:从梯形的顶至底的m条路径允许在数字结点相交或边相交。

    对于给定的数字梯形,分别按照规则1,规则2,和规则3 计算出从梯形的顶至底的m
    条路径,使这m条路径经过的数字总和最大。

    第1 行中有2个正整数m和n(m,n<=20),分别
    表示数字梯形的第一行有m个数字,共有n 行。接下来的n 行是数字梯形中各行的数字。
    第1 行有m个数字,第2 行有m+1 个数字,…。

    将按照规则1,规则2,和规则3 计算出的最大数字总和输出

    每行一个最大总和。

    2 5
    2 3
    3 4 5
    9 10 9 1
    1 1 10 1 1
    1 1 10 12 1 1

    66
    75
    77

  • 相关阅读:
    struts2的结果类型
    struts2的基本配置
    struts2拦截器
    struts2入门程序
    struts2工作原理
    Mysql处理字符串函数(转)
    oracle调用java方法的例子(下面所有代码都是在sql/plus中写)
    Oracle数据库中调用Java类开发存储过程、函数的方法
    Oracle触发器反向调用Java程序
    javaweb项目打包成war包
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/3935296.html
Copyright © 2011-2022 走看看