线性规划单纯型法(bzoj 3112: [Zjoi2013]防守战线)

线性规划:

给出若干个不等式,再给出个函数G(),求出G()的极值

例如①x2+x3>=1;②x1+x2+x3+x4+x5>=4;③x3+x4+x5>=2

G() = x1+5*x2+6*x3+3*x4+4*x5,求G()的最小值


部分的线性规划问题可以用网络流来解决

如果原不等式系数矩阵每一列上不为0的相同且连续,那么就可以

先看上面那个例子的系数矩阵:

1  5  6  3  4(求min)

    1  1          1(>=)

1  1  1  1  1  4

        1  1  1  2

虽然这个系数矩阵满足上面的要求,但它是行连续的,仅这个样例满足而已

可以把这个矩阵翻转90°

1  4  2(求max)

    1      1(<=)

1  1      5

1  1  1  6

    1  1  3

    1  1  4

一定满足要求!它对应的新多项式为①y2<=1;②y1+y2<=5;③y1+y2+y3<=6;④y2+y3<=3;⑤y2+y3<=4(没意义)

G() = y1+4*y2+2*y3,求G()的最大值


到这里问题就变得可解了,下一步:将原不等式加入松弛变量后进行差分

①y2+y4=1;②y1+y2+y5=5;③y1+y2+y3+y6=6;④y2+y3+y7=3;⑤0=0

然后拿所有式子减去上一个式子

①y2+y4=1;②y1+y5-y4=4;③y3+y6-y5=1;④y7-y1-y6=-3;⑤-y2-y3-y7=-3

因为原先系数矩阵每一列上不为0的相同且连续,所以在这里每个变量一定只出现2次,一次正一次负,相当于网络流流量守恒,所以可以将每个式子当成一个点,具体建边如下:

①如果第i个等式常数C为正,那么从源点连一条容量为C,费用为0的边,常数C为负则连一条容量为|C|,费用为0的边到汇点

②对于每个变量yi,从它系数为正的式子向系数为负的式子连一条容量为inf,费用为G()里系数的边

这就成了单纯型的网络流,不需要满流求最大费用即可




题解:其实已经在上面了,样例就是例子

http://www.lydsy.com/JudgeOnline/problem.php?id=3112

单纯型可以转成最大费用最大流,将所有与源点直接相连的点再连一条一模一样的边到汇点就好了

但是复杂度比较高,常数大,而且难写,这里有个更简单的方法,只要将对偶的矩阵代入即可求得!

因为篇幅太长,这里就引用一篇文章吧:http://www.cnblogs.com/ScratchingBear/p/5634692.html

还有https://wenku.baidu.com/view/367ab2292f60ddccda38a033.html

a[i][j]:

    1  4  2

1      1

5  1  1

6  1  1  1

3      1  1

4      1  1


#include<stdio.h>
#define inf 2147483647
int n, m, a[1010][10010], net[10010];
void Pivot(int l, int e)
{
	int i, j, last;
	last = -1;
	for(i=0;i<=m;i++)
	{
		if(a[l][i])
		{
			net[i] = last;
			last = i;
		}
	}
	for(i=0;i<=n;i++)
	{
		if(a[i][e]==0 || i==l)
			continue;
		for(j=last;j!=-1;j=net[j])
		{
			if(j==e)
				continue;
			a[i][j] -= a[i][e]*a[l][j];
		}
		a[i][e] = -a[i][e];
	}
}

int SimpleX()
{
	int now, temp, k, i;
	while(1)
	{
		now = 0;
		for(i=1;i<=m;i++)
		{
			if(a[0][i]>0)
			{
				now=i;
				break;
			}
		}
		if(now==0)
			return -a[0][0];
		k = inf;
		for(i=1;i<=n;i++)
		{
			if(a[i][now]>0 && a[i][0]<k)
			{
				temp = i;
				k = a[i][0];
			}
		}
		Pivot(temp, now);
	}
}

int main(void)
{
	int ans;
	int x, y, j, i;
	scanf("%d%d", &n, &m);
	for(i=1;i<=n;i++)
		scanf("%d", &a[i][0]);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d%d", &x, &y, &a[0][i]);
		for(j=x;j<=y;j++)
			a[j][i] = 1;
	}
	ans = SimpleX();
	printf("%d\n", ans);
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值