[单纯形 模板题] UOJ #179 线性规划

本文探讨了在解决特定线性规划问题时,对于转轴操作中变量选取方式的改进。通过采用一种新的策略来选择正系数中的变量,有效地避免了被卡死循环的问题,并通过代码示例详细说明了实现过程。

关于转轴操作选取变量的时候
之前我是这样的

for (int i=1;i<=n;i++) if (a[0][i]>eps) { e=i; break; }

但是这个东西在另一道题T了
那我就尴尬了 怎么弄都弄不过去 只好求助 在rxd和lnj两位大佬的姿势的熏陶下 就这样了

double mx=eps;
for (int i=1;i<=n;i++) if (a[0][i]>mx) { mx=a[0][i]; e=i; }

然后那题就过了
不过在这里没什么卵用 n才20 从0ms变成了1ms

据说这两种都是会被卡死循环的 但是算导上说应该取正系数中编号最小的变量?我没有考证过 反正考试的时候乱写出题人应该卡不掉吧?卡掉我就认了

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

const int N=25,M=25;
const double eps=1e-8;

inline int ran(){
  static int x=31253125; x+=(x<<4)+1; return x&65536;
}

int n,m; int idx[N],idy[M];
double a[M][N],Ans[N];
int next[N];

inline void PIVOT(int l,int e){
  swap(idx[e],idy[l]); int last=-1;
  for (int i=0;i<=n;i++) if (i!=e) a[l][i]/=a[l][e];
  a[l][e]=1/a[l][e];
  memset(next,-1,sizeof(next));
  for (int i=0;i<=n;i++) if (i!=e && (a[l][i]>eps||a[l][i]<-eps)) next[i]=last,last=i;
  for (int i=0;i<=m;i++){
    if (i==l || (a[i][e]<eps && a[i][e]>-eps)) continue;
    for (int j=last;~j;j=next[j])
      a[i][j]-=a[i][e]*a[l][j];
    a[i][e]=-a[i][e]*a[l][e];    
  }
}

int main(){
  int op;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  scanf("%d%d%d",&n,&m,&op);
  for (int i=1;i<=n;i++) scanf("%lf",&a[0][i]);
  for (int i=1;i<=m;i++) { for (int j=1;j<=n;j++) scanf("%lf",&a[i][j]); scanf("%lf",&a[i][0]); }
  for (int i=1;i<=n;i++) idx[i]=i;
  for (int i=1;i<=m;i++) idy[i]=n+i;
  while (1){
    int l=0,e=0;
    for (int i=1;i<=m;i++) if (a[i][0]<-eps && (!l||rand()&1)) l=i;
    if (!l) break;
    for (int i=1;i<=n;i++) if (a[l][i]<-eps && (!e||rand()&1)) e=i;
    if (!e) return printf("Infeasible\n"),0;
    PIVOT(l,e);
  }
  while (1){
    int l=0,e=0; double mn=1e15; double mx=eps;
    for (int i=1;i<=n;i++) if (a[0][i]>mx) { mx=a[0][i]; e=i; }
    if (!e) break;
    for (int i=1;i<=m;i++)
      if (a[i][e]>eps && a[i][0]/a[i][e]<mn)
    mn=a[i][0]/a[i][e],l=i;
    if (!l) return printf("Unbounded\n"),0;
    PIVOT(l,e);
  }
  printf("%.8lf\n",-a[0][0]);
  if (op){
    for (int i=1;i<=m;i++) if (idy[i]<=n) Ans[idy[i]]=a[i][0];
    for (int i=1;i<=n;i++) printf("%.8lf ",Ans[i]);
  }
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值