分析:建图的方法还是比较难想。 首先,计算两个相邻时刻的差分,若a[i] < a[i-1],就从i向汇点连边,容量为a[i-1]-a[i],若a[i] > a[i-1],就从源点向i连边,容量为a[i]-a[i-1] 请联系差分数组理解。 然后,对于志愿者,连s->(t+1),容量为+∞,费用为c。 最后,由于志愿者可以多不能少,连(i+1)->i,容量为+∞。 跑费用流算出费用即可。
#include#include #include #include #define MAXN 1000#define MAXM 10000#define INF 0x7f7f7f7f7f7f7f7fllusing namespace std;deque q;void Read(int &x){ char c; while(c=getchar(),c!=EOF) if(c>='0'&&c<='9'){ x=c-'0'; while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0'; ungetc(c,stdin); return; }}int n,m,S,T,a[MAXN+10];typedef long long LL;LL dist[MAXN+10],cost;bool vis[MAXN+10];struct node{ int wt,v; LL cap; node *next,*back;}*adj[MAXN+10],edge[MAXN*4+MAXM*2+10],*ecnt=edge,*pre[MAXN+10];template void Read(T &x){ char c; while(c=getchar(),c!=EOF) if(c>='0'&&c<='9'){ x=c-'0'; while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0'; ungetc(c,stdin); return; }}void addedge(int u,int v,int wt,LL cap){ node *p=++ecnt; p->v=v; p->wt=wt; p->cap=cap; p->next=adj[u]; adj[u]=p; p=p->back=++ecnt; p->v=u; p->wt=-wt; p->cap=0; p->next=adj[v]; adj[v]=p; p->back=ecnt-1;}void read(){ Read(n),Read(m); int i,s,t,c; T=n+2; for(i=1;i<=n;i++) Read(a[i]); for(i=1;i<=n;i++){ if(a[i]>a[i-1]) addedge(S,i,0,a[i]-a[i-1]); else if(a[i] sum){ q.push_back(u); continue; } len--; sum-=dist[u]; vis[u]=0; for(node *p=adj[u];p;p=p->next){ v=p->v; if(p->cap>0&&dist[v]>dist[u]+p->wt){ dist[v]=dist[u]+p->wt; pre[v]=p; if(!vis[v]){ vis[v]=1; if(q.empty()||dist[v]>dist[q.front()]) q.push_back(v); else q.push_front(v); sum+=dist[v]; len++; } } } } if(dist[T]==INF) return 0; return 1;}void mcmf(){ int u; LL delta; while(spfa()){ delta=INF; for(u=T;u!=S;u=pre[u]->back->v) delta=min(delta,pre[u]->cap); for(u=T;u!=S;u=pre[u]->back->v){ pre[u]->cap-=delta; pre[u]->back->cap+=delta; cost+=delta*pre[u]->wt; } }}int main(){ read(); mcmf(); printf("%lld\n",cost);}