单纯形解线性规划

27 篇文章 0 订阅
1 篇文章 0 订阅

1、noi2008 志愿者招募

这题更广为人知的解法是网络流建模,题解戳这里

其实可以拿线性规划水过去

单纯形讲义戳这里

网上好多程序据若都没看懂,最后才知道他们套用了一个叫做对偶性的定理。。。。

大概就是长这样:


(证明就不放了,都可以在算导上找到)

所以这题就可以水过去了。。。。

速度确实比裸的spfa求网络流快

code

//最小费用最大流
#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
const int Maxn=1005, Maxm=100000;
const int INF = 2147483647;
typedef long long LL;
LL ans, d[Maxn], cost[Maxm];
int q[Maxm],node[Maxm],next[Maxm],a[Maxn],flow[Maxm];
int n,m,s,t,w,i,N,minx,l,r,tot,x[Maxn],path[Maxn],way[Maxn];
int v[Maxn];
 
void add(int x,int y,int z1,int z2){
  node[++tot]=y; next[tot]=a[x]; a[x]=tot; flow[tot]=z1; cost[tot]=z2;
  node[++tot]=x; next[tot]=a[y]; a[y]=tot; flow[tot]=0; cost[tot]=-z2;
}
 
bool spfa(){
  d[0] = INF; d[0] *= d[0];
  for (i=1;i<=N;i++) d[i] = d[0];
  d[1] = 0;
  memset(v,0,sizeof(v));
  for (q[l=r=1]=1,v[1]=1;l<=r;v[q[l++]]=0)
    for (i=a[q[l]];i;i=next[i])
    if (flow[i]>0 && d[node[i]]>d[q[l]]+cost[i]){
      d[node[i]] = d[q[l]]+cost[i];
      path[node[i]] = q[l]; way[node[i]] = i;
      if (v[node[i]]==0) v[q[++r]=node[i]]=0;
    }
  if (d[N]==d[0]) return 0;
  minx = INF;
  for (i=N;i>1;i=path[i])
    minx = min(minx, flow[way[i]]);
  ans += d[N]*(LL)minx;
  for (i=N;i>1;i=path[i])
    flow[way[i]]-=minx, flow[way[i]^1]+=minx;
  return 1;
}
 
int main(){
  //freopen("employee.in","r",stdin);
  //freopen("employee.out","w",stdout);
  scanf("%d%d",&n,&m);
  for (i=1;i<=n;i++)
    scanf("%d",&x[i]);
  for (i=1,tot=1;i<=m;i++){
    scanf("%d%d%d",&s,&t,&w);
    add(s+1,t+1+1,INF,w);
  }
  for (i=1;i<=n;i++)
    add(i+1+1,i+1,INF,0);
  for (i=1,N=n+3;i<=n+1;i++)
  if (x[i]-x[i-1]>0)
    add(1,i+1,x[i]-x[i-1],0);
  else add(i+1,N,x[i-1]-x[i],0);
  while (spfa());
  printf("%lld\n",ans);
  return 0;
}

//单纯形
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
const double Eps=1e-12,INF=1e12;
double a[10005][1005],b[10005],c[1005],cnt;
int n,m,i,j,s,t,ans;
 
void Pivot(int l,int e){
  b[l]/=a[l][e];
  for (int i=1;i<=n;i++)
    if (i!=e) a[l][i]/=a[l][e];
  a[l][e]=1/a[l][e];
   
  for (int i=1;i<=m;i++)
  if (i!=l && fabs(a[i][e])>Eps){
    b[i]-=b[l]*a[i][e];
    for (int j=1;j<=n;j++)
    if (j!=e) a[i][j]-=a[l][j]*a[i][e];
    a[i][e]=-a[i][e]*a[l][e];
  }
   
  cnt+=c[e]*b[l];
  for (int i=1;i<=n;i++)
  if (i!=e) c[i]-=c[e]*a[l][i];
  c[e]=-c[e]*a[l][e];
}
 
double simplex(){
  int i, j, e, l;
  while (true){
    for (i=1;i<=n;i++)
    if (c[i]>Eps) break;
    if ( (e=i)==n+1 ) return cnt;
    double tmp=INF;
    for (i=1;i<=m;i++)
      if (a[i][e]>Eps && tmp>b[i]/a[i][e])
        tmp = b[i]/a[i][e], l=i;
    if (tmp==INF) return INF;
    Pivot(l,e);
  }
  return INF;
}
 
int main(){
  //freopen("employee.in","r",stdin);
  //freopen("employee.out","w",stdout);
  scanf("%d%d",&n,&m);
  for (i=1;i<=n;i++)
    scanf("%lf",&c[i]);
  for (i=1;i<=m;i++){
    scanf("%d%d%lf",&s,&t,&b[i]);
    for (j=s;j<=t;j++) a[i][j]=1;
  }
  ans = (int) (simplex()+0.5);
  printf("%d\n",ans);
  return 0;
}


2、zjoi2013 防守战线

还是裸题吧,套用对偶性转化后直接上模版

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
 
using namespace std;
const double Eps=1e-12,INF=1e12;
double a[1005][10005],b[1005],c[10005],cnt;
int n,m,i,j,s,t;
long long ans;
 
void Pivot(int l,int e){
  b[l]/=a[l][e];
  for (int i=1;i<=n;i++)
    if (i!=e) a[l][i]/=a[l][e];
  a[l][e]=1/a[l][e];
   
  for (int i=1;i<=m;i++)
  if (i!=l && fabs(a[i][e])>Eps){
    b[i]-=b[l]*a[i][e];
    for (int j=1;j<=n;j++)
    if (j!=e) a[i][j]-=a[l][j]*a[i][e];
    a[i][e]=-a[i][e]*a[l][e];
  }
   
  cnt+=c[e]*b[l];
  for (int i=1;i<=n;i++)
  if (i!=e) c[i]-=c[e]*a[l][i];
  c[e]=-c[e]*a[l][e];
}
 
double simplex(){
  int i, j, e, l;
  while (true){
    for (i=1;i<=n;i++)
    if (c[i]>Eps) break;
    if ( (e=i)==n+1 ) return cnt;
    double tmp=INF;
    for (i=1;i<=m;i++)
      if (a[i][e]>Eps && tmp>b[i]/a[i][e])
        tmp = b[i]/a[i][e], l=i;
    if (tmp==INF) return INF;
    Pivot(l,e);
  }
  return INF;
}
 
int main(){
  //freopen("defend.in","r",stdin);
  //freopen("defend.out","w",stdout);
  scanf("%d%d",&m,&n);
  for (i=1;i<=m;i++) scanf("%lf",&b[i]);
  for (i=1;i<=n;i++){
    scanf("%d%d%lf",&s,&t,&c[i]);
    for (j=s;j<=t;j++) a[j][i]=1;
  }
  ans = (long long) (simplex()+0.5);
  printf("%lld\n",ans);
  return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值