洛谷 P3337 [ZJOI2013]防守战线 单纯形

题目描述

战线可以看作一个长度为n 的序列,现在需要在这个序列上建塔来防守敌兵,在序列第i 号位置上建一座塔有Ci 的花费,且一个位置可以建任意多的塔,费用累加计算。有m 个区间[L1, R1], [L2, R2], …, [Lm, Rm],在第i 个区间的范围内要建至少Di 座塔。求最少花费。

输入输出格式

输入格式:
第一行为两个数n, m。

接下来一行,有n 个数,描述C 数组。

接下来m 行,每行三个数Li,Ri,Di,描述一个区间。

输出格式:
仅包含一行,一个数,为最少花费。

输入输出样例

输入样例#1:
5 3
1 5 6 3 4
2 3 1
1 5 4
3 5 2
输出样例#1:
11
说明

【样例说明】

位置1 建2 个塔,位置3 建一个塔,位置4 建一个塔。花费1*2+6+3=11。

【数据规模】

对于20%的数据,n≤20,m≤20。

对于50%的数据(包括上部分的数据),Di 全部为1。

对于70%的数据(包括上部分的数据),n≤100,m≤1000。

对于100%的数据,n≤1000,m≤10000,1≤Li≤Ri≤n,其余数据均≤10000。

分析:
显然对于每一个区间都可以写出一个约束,
x l + x l + 1 + . . . + x r > = d i x_{l}+x_{l+1}+...+x_r>=d_i xl+xl+1+...+xr>=di
要最小化 ∑ i = 1 n c i ∗ x i \sum_{i=1}^{n}c_i*x_i i=1ncixi
我们可以化成对偶线性规划。
需要把系数矩阵颠倒,即 A ′ [ j ] [ i ] = A [ i ] [ j ] A'[j][i]=A[i][j] A[j][i]=A[i][j]
然后约束全部反号,不等式右边变为 c i c_i ci
然后最大化 ∑ i = 1 n b i ∗ x i \sum_{i=1}^{n}b_i*x_i i=1nbixi
然后就是标准形式,直接单纯形即可。

代码:

#include<iostream>
#include<cstdio>
#include <cmath>
#include<algorithm>

const int maxn=1e3+7;
const int maxm=1e4+7;
const int inf=0x3f3f3f3f;

using namespace std;

int n,m;
int a[maxn][maxm],next[maxm];

void pivot(int l,int e)
{
    for (int i=0;i<=n;i++)
    {
        if ((!a[i][e]) || (i==l)) continue;
        for (int j=0;j<=m;j++)
        {
            if ((!a[l][j]) || (j==e)) continue;
            a[i][j]-=a[i][e]*a[l][j];
        }
        a[i][e]=-a[i][e];
    }
}

int simplex()
{
    while (1)
    {
        int now=0;
        for (int i=1;i<=m;i++)
        {
        	if (a[0][i]>0) 
			{ 
			    now=i; 
				break; 
			}
		}
        if (now==0) return -a[0][0];
        int tmp,minn=inf;
        for (int i=1;i<=n;i++)
        {
            if ((a[i][now]>0) && (a[i][0]<minn))
            {
                tmp=i;
                minn=a[i][0];
            }
        }
        pivot(tmp,now);
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&a[i][0]);
    int x,y;
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&a[0][i]);
        for (int j=x;j<=y;j++) a[j][i]=1;
    }  
    int ans=simplex();
    printf("%d\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值