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; }