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;
}
还是裸题吧,套用对偶性转化后直接上模版
#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;
}