【代码系列】POJ 1273( 单纯形解最大流 )

这是写的很蠢但是很详细的单纯形,为什么蠢呢?因为我竟然给每个基变量开了空间!!写完之后,又看了盾哥的代码!!然后……

// 题目来源:POJ 1273
// 题目大意:求源汇点最大流
// 解决方法:建立线性规划模型,用单纯形求解即可
// 特别注意:设原图有n个点,m条边,则:最大流量约束m,流量平衡约束2*n-4,总约束m+2*n-4;非基变量m,基变量m+2*n-4,总变量2*m+2*n-4;

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define eps 0.000001
using namespace std;

double a[1002][1002];
int num[1002];
int n, m;

int main( )
{
    while( scanf( "%d%d", &m, &n ) > 0 )
    {
        memset( a, 0, sizeof( a ) );  // 目标约束矩阵初始化
        int aa, bb, cc;
        for( int i = 1; i <= m; i++ )
        {
            scanf( "%d%d%d", &aa, &bb, &cc ); // 读取出端点aa,入端点bb,流量限制cc 
            a[i][0] = cc;  
            a[i][i] = -1;  // 约束矩阵加入最大流量限制
            if( aa != 1 ) a[m+aa-1][i] = -1, a[m+n-3+aa][i] = 1;
            if( bb != n ) a[m+bb-1][i] = 1,  a[m+n-3+bb][i] = -1;  // 约束矩阵加入流量平衡限制
            if( aa == 1 ) a[0][i] = 1;  // 矩阵首行加入目标函数
        }
        int var = 2*m + 2*n - 4, bnd = m + 2*n - 4;  // 设置变量(variable)与约束(bound)的数量
        for( int i = 1; i <= bnd; i++ )
            num[i] = i + m;  // 给每一约束条件规定基变量
        while( 1 )
        {
            int line = 0, row = 0;  // line得到进基变量所在列,row得到出基变量所在行
            double delta = 1e30;  // 取最紧约束值 
            for( line = 1; line < var+1; line++ )
                if( a[0][line] > eps ) break;  // 在目标函数值中找到进基变量
            if( line == var+1 ) break;  // 无进基变量退出循环
            for( int i = 1; i <= bnd; i++ )
                if( a[i][line] < -eps && a[i][0] / -a[i][line] < delta )
                    delta = a[i][0] / -a[i][line], row = i;  // 找到被进基变量约束得最紧的基变量,标记为出基变量
            a[row][ num[row] ] = -1;  // 移项将出基变量改为非基变量
            for( int i = 0; i <= var; i++ )
                if( i != line ) a[row][i] /= -a[row][line];  // 对出基变量所代表的约束执行恒等变换
            a[row][line] = 0;  // 取消进基变量的非基属性
            num[row] = line;  // 给进基变量标记为基变量并代表一个当前约束
            for( int i = 0; i <= bnd; i++ )
                if( i != row && a[i][line] != 0 )
                {
                    for( int j = 0; j <= var; j++ )
                        if( j != line ) a[i][j] += a[row][j] * a[i][line];  // 对所有约束执行消元
                    a[i][line] = 0;  // 消元操作
                }
        }
        printf( "%.0lf\n", a[0][0] );  // 输出目标函数当前值
    }
    return 0;
}

诶,还是太蠢了,反正需要利用的只有非基变量,也就是说每次需要表示出来的变量总是那么多!那我恒等变形的时候只要覆盖就可以了嘛!!再跪盾哥。

优化后的code:

// 题目来源:POJ 1273
// 题目大意:求源汇点最大流
// 解决方法:建立线性规划模型,用单纯形求解即可
// 特别注意:设原图有n个点,m条边,则:最大流量约束m,流量平衡约束2*n-4,总约束m+2*n-4;变量m;

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#define eps 0.000001
using namespace std;

double a[1002][1002];
int num[1002];
int n, m;

int main( )
{
    while( scanf( "%d%d", &m, &n ) > 0 )
    {
        memset( a, 0, sizeof( a ) );  // 目标约束矩阵初始化
        int aa, bb, cc;
        for( int i = 1; i <= m; i++ )
        {
            scanf( "%d%d%d", &aa, &bb, &cc ); // 读取出端点aa,入端点bb,流量限制cc 
            a[i][0] = cc;  
            a[i][i] = -1;  // 约束矩阵加入最大流量限制
            if( aa != 1 ) a[m+aa-1][i] = -1, a[m+n-3+aa][i] = 1;
            if( bb != n ) a[m+bb-1][i] = 1,  a[m+n-3+bb][i] = -1;  // 约束矩阵加入流量平衡限制
            if( aa == 1 ) a[0][i] = 1;  // 矩阵首行加入目标函数
        }
        int bnd = m + 2*n - 4;  // 设置约束(bound)的数量
        while( 1 )
        {
            int line = 0, row = 0;  // line得到进基变量所在列,row得到出基变量所在行
            double delta = 1e30, k;  // 取最紧约束值 
            for( line = 1; line < m+1; line++ )
                if( a[0][line] > eps ) break;  // 在目标函数值中找到进基变量
            if( line == m+1 ) break;  // 无进基变量退出循环
            for( int i = 1; i <= bnd; i++ )
                if( a[i][line] < -eps && a[i][0] / -a[i][line] < delta )
                    delta = a[i][0] / -a[i][line], row = i;  // 找到被进基变量约束得最紧的基变量,标记为出基变量
            k = -a[row][line];  // 取进基变量系数的相反数
            a[row][line] = -1;  // 将出基变量覆盖进基变量完成出基操作
            for( int i = 0; i <= m; i++ )
                a[row][i] /= k;  // 对出基变量所代表的约束执行恒等变换
            for( int i = 0; i <= bnd; i++ )
                if( i != row && a[i][line] != 0 )
                {
                    k = a[i][line], a[i][line] = 0;
                    for( int j = 0; j <= m; j++ )
                        a[i][j] += a[row][j] * k;  // 对所有约束执行消元
                }
        }
        printf( "%.0lf\n", a[0][0] );  // 输出目标函数当前值
    }
    return 0;
}
这样优化以后,空间瞬间变为原来的四分之一(大约嗯),虽然我的数组还是开了这么多。

本来觉得吧,单纯形这打起来蛋疼的算法,到底为什么值得我们去学习呢,空间消耗这么大!原来……是我打的太丑了,这样弄一下好了不知道多少了啊!!爱打多了!!


猜你喜欢

转载自blog.csdn.net/JarjingX/article/details/8565910
今日推荐