SRM 285 DIV1 Hard Distincter
問題
数列sequenceが与えられる。
「どれかを1増加させる」「どれかを1減少させる」という操作ができる。なお、マイナスにすることもできる。
最低K個の相違なる数を含ませるようにするとき、操作回数を最小化せよ
- 1 <= |sequence| <= 50
- 1 <= sequence[i] <= 1000
- 1 <= K <= |sequence|
解答
Editorialを見た。
まずソートする。作る数はこの順序を変えなくてもいいらしい(わかってない)。
なのでDPする。(i * この数より大きくする * 残りK)という感じで。数だけ増加させてiを変えないようにすれば状態ごとの処理がO(1)でできる。
数の範囲はたかだか(-|sequence|/2〜1000+|sequence|/2)くらいなのでできる。
他に最小費用流を使う解答もある。フォーラム参照。
可能な数のノードとsequenceのノード間にabs(sequence[i]-x)のコストの辺を張る。辺の容量を1にしてフローをK流せばいい。
計算量が少し大きいが、間に合うらしい。
コメント
順序を変えなくてもいいというのはわかってないけれど、なんとなくそうでありそうだよね。
"相違なる"からフローの容量を連想できる。
普通に解けるべき問題だと思った
コード
#include <vector> #include <cstring> #include <algorithm> #define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i)) #define all(o) (o).begin(), (o).end() #define mset(m,v) memset(m,v,sizeof(m)) #define INF 0x3f3f3f3f using namespace std; typedef vector<int> vi; int dp[55][1201][2][55]; struct Distincter { vi s; int n; int rec(int i, int x, bool gt, int k) { if(x > 1200) return INF; if(dp[i][x][gt][k] != -1) return dp[i][x][gt][k]; int r = INF; if(i == n) { r = k == 0 ? 0 : INF; }else { r = min(r, rec(i, x+1, true, k)); r = min(r, abs(s[i] - x) + rec(i+1, x, false, max(0, k-gt))); } return dp[i][x][gt][k] = r; } int disperse(vector <int> s_, int K) { s = s_, n = s.size(), sort(all(s)); rep(i, n) s[i] += 55; mset(dp, -1); return rec(0, 0, true, K); } };