読者です 読者をやめる 読者になる 読者になる

負辺を含む最小費用流について(Attack the Moles)

蟻本での最小費用流の扱い方としては,
1.毎回Bellman-Fordで残余グラフ上を見る
2.ポテンシャルhを考えるとdijkstraで出来る
3.(column)負辺ののぞき方紹介(使う本数が必ず等しい場合,負辺を流しきったと考えた時の変形,あとはBellman-Fordを使おうとも書いてある)

今、頂点数NのDAGがあって流量Fの小さい"最大"費用流を求めたい気分だとして、3の"負辺を流しきる変形"をやると、流すフローがNくらい増えるので、オーダーは(F+N)ElogVでN^3くらいになってしまう.

2のdijkstraだが、蟻本をよく読むと"また、負の辺が存在しなければh0の計算にはダイクストラ法を用いることができます。"と書いてある(hiは流量iの最小費用流の残余グラフ上でのsから各vへの距離)
もちろんh0はそのまま最短路問題なので、負辺があればBellman-Fordでも大丈夫(ここではDAGなのでトポロジカルに見るだけ).そこから先は、実はdijkstraで出来る.(蟻本のh_i+1をh_iから計算するところに、元の辺のコストが0以上であることは使われていない)

よってポテンシャルの初期値を先に計算し、あとは最小費用流のコードそのままで負辺を含む最小費用流を計算できます

蟻本に明示的に書いてなかったのでびっくりした(2015.7/12 追記 書いてありました 目ついてますか)

↓Attack the Molesのコード

#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#include <cmath>
#include <algorithm>
#include <functional>
#include <utility>
using namespace std;
#define rep(i,n) for(int i=0;i<(n);++i)
#define rep1(i,n) for(int i=1;i<=(n);++i)
#define all(c) (c).begin(),(c).end()
#define pb push_back
#define fs first
#define sc second
#define show(x) cout << #x << " = " << x << endl
typedef pair<int,int> P;
struct edge {int to,cap,cost,rev;};
const int MAX_V=6100,INF=1e9;
int V;			//代入!!
vector<edge> G[MAX_V];
int h[MAX_V];
int dist[MAX_V];
int prevv[MAX_V],preve[MAX_V];
int top[MAX_V];
int s,t,l,r;
void add_edge(int from, int to, int cap, int cost){
//	printf("%d->%d  cap=%d,cost=%d\n",from,to,cap,cost);
	edge e1={to,cap,cost,G[to].size()},e2={from,0,-cost,G[from].size()};
	G[from].push_back(e1);
	G[to].push_back(e2);
}
int min_cost_flow(int s, int t, int f){
	int res=0;
	fill(h,h+V,0);
	rep(i,V){
		int v=top[i];
		rep(j,G[v].size()){
			edge &e=G[v][j];
			if(e.cap==0) continue;
			int u=e.to;
			h[u]=min(h[u],h[v]+e.cost);
		}
	}
//	rep(i,V) printf("h[%d]=%d\n",i,h[i]);
	while(f>0){
		priority_queue< P,vector<P>,greater<P> > que;
		fill(dist,dist+V,INF);
		dist[s]=0;
		que.push(P(0,s));
		while(!que.empty()){
			P p=que.top();
			que.pop();
			int v=p.second;
			if(dist[v]<p.first) continue;
			for(int i=0;i<G[v].size();i++){
				edge &e=G[v][i];
				if(e.cap>0 && dist[e.to]>dist[v]+e.cost+h[v]-h[e.to]){
					dist[e.to]=dist[v]+e.cost+h[v]-h[e.to];
					prevv[e.to]=v;
					preve[e.to]=i;
					que.push(P(dist[e.to],e.to));
				}
			}
		}
		if(dist[t]==INF) return -1;
		for(int v=0;v<V;v++) h[v]+=dist[v];
		int d=f;
		for(int v=t;v!=s;v=prevv[v]){
			d=min(d,G[prevv[v]][preve[v]].cap);
		}
		f-=d;
		res+=d*h[t];
		for(int v=t;v!=s;v=prevv[v]){
			edge &e=G[prevv[v]][preve[v]];
			e.cap-=d;
			G[v][e.rev].cap+=d;
		}
	}
	return res;
}
int X[3000],T[3000],p[3000];
vector<P> id;
int main(){
	int N,speed,xl,xr;
	scanf("%d%d%d%d",&N,&speed,&xl,&xr);
	rep(i,N) scanf("%d%d%d",X+i,T+i,p+i);
	s=2*N,t=s+1,l=s+2,r=s+3;
	V=r+1;
	rep(i,N){
		id.pb(P(T[i]*2,i));
		id.pb(P(T[i]*2+1,i+N));
	}
	id.pb(P(-2,s));
	id.pb(P(-1,l));
	id.pb(P(-1,r));
	id.pb(P(INF,t));
	sort(all(id));
	rep(i,V) top[i]=id[i].sc;
	rep(i,N) add_edge(i,i+N,1,-p[i]),add_edge(i,i+N,1,0);
	rep(i,N) rep(j,N){
		if(i==j) continue;
		if((T[j]-T[i])*speed>=abs(X[j]-X[i])) add_edge(i+N,j,2,0);
	}
	rep(i,N){
		if(T[i]*speed>=abs(X[i]-xl)) add_edge(l,i,1,0);
		if(T[i]*speed>=abs(X[i]-xr)) add_edge(r,i,1,0);
	}
	rep(i,N) add_edge(i+N,t,2,0);
	add_edge(s,t,2,0);
	add_edge(s,l,1,0);
	add_edge(s,r,1,0);
	cout<<-min_cost_flow(s,t,2)<<endl;
}