UTPC 2011 問題 H : キャッシュ戦略
UTPC 2011 問題 H : キャッシュ戦略
http://www.utpc.jp/2011/problems/cache.html
問題概要
日本語でとても簡潔なため略
解法
最小費用流
解説に詳しく、どのようにグラフを作れば解けるか乗っているので参照。
http://www.utpc.jp/2011/
しかし、負の辺があり、かつ、ノードが10000個あり、
エッジが20000個くらいになりそうなので、なかなか難しい。
まず、負の辺を含むためポテンシャルを求めなければならない。
しかし、ベルマンフォードはサイズ的に苦しい。
->簡単なDPで求める。この問題は一直線に並んだノードにシンク方向にエッジが伸びているだけなので、簡単に求まる。
同じ玉が2個続いた場合、普通にエッジを足すと負の閉路になってしまう
- >そのときだけ別処理
ソース
#include<iostream> #include<vector> #include<queue> #define REP(i,b,n) for(int i=b;i<(int)n;i++) #define rep(i,n) REP(i,0,n) using namespace std; const int INF = 1000000000; const int N = 10001; class State{ public: int cost, pos; bool operator<(const State &s)const{ return cost > s.cost; } }; class Edge{ public: int to, cap, cost, rev; }; int V, h[N], dist[N], prevv[N], preve[N]; vector<Edge> G[N]; void addEdge(int from, int to, int cap, int cost){ G[from].push_back((Edge){to, cap, cost, G[to].size()}); G[to].push_back((Edge){from, 0, -cost, G[from].size()-1}); } int min_cost_flow(int s, int t, int f){ int ret = 0; while(f>0){ priority_queue<State> Q; rep(i, V)dist[i] = INF; dist[s] = 0; Q.push((State){0, s}); while(!Q.empty()){ State now = Q.top(); Q.pop(); int v = now.pos; if(dist[v] < now.cost)continue; rep(i, G[v].size()){ 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; Q.push((State){dist[e.to], e.to}); } } } if(dist[t] == INF)return -1; rep(v, V)h[v] += dist[v]; int mini = f; for(int v = t;v != s; v = prevv[v]){ mini = min(mini, G[prevv[v]][preve[v]].cap); } f -= mini; ret += mini * h[t]; for(int v = t; v != s; v= prevv[v]){ Edge &e = G[prevv[v]][preve[v]]; e.cap -= mini; G[v][e.rev].cap += mini; } } return ret; } int main(){ int m, n, k; cin >> m >> n >> k; int w[N], order[N], sum = 0; rep(i, n){ cin >> w[i]; } rep(i, k){ cin >> order[i]; order[i]--; if(i > 0 && order[i] == order[i-1]) continue; sum += w[order[i]]; } int last[N]; rep(i, n){ last[i] = -1; } V = k; int mini = 0; rep(i, k){ if(last[order[i]] != -1){ if(last[order[i]] + 1 != i) addEdge(last[order[i]] + 1, i, 1, -w[order[i]]); mini -= w[order[i]]; } last[order[i]] = i; if(i != 0) addEdge(i-1, i, INF, 0); h[i] = mini; } cout << sum + min_cost_flow(0, k-1, m-1) << endl;; return 0; }