SRM 205 DIV1 Hard LongPipes
問題
数列segmentsが与えられる。何個かを組み合わせて、和を特定の数desiredLengthにしたい。
組み合わせる数を最小にせよ。
- 1<=|segments|<=38
- 1<=segments[i]<=10^12
- 1<=desiredLength<=38*10^12
解答
Exact-Knapsack。半分全列挙する。
2^(N/2)*N/2でできる。
このやり方は最初は意味不明だったが、しっかり考えればわかった。
コメント
最初、このやり方の「同じ重さの所はまとめる」というところがよくわかってなくてFailedしてしまった。今はわかったと思う。良かった
コード
#include <string> #include <vector> #include <algorithm> #include <numeric> #include <set> #include <map> #include <queue> #include <iostream> #include <sstream> #include <cstdio> #include <cmath> #include <ctime> #include <cstring> #include <cctype> #define rep(i,n) for(int (i)=0;(i)<(int)(n);++(i)) #define each(it,o) for(__typeof((o).begin()) it = (o).begin(); it != (o).end(); ++ it) #define pb(x) push_back(x) #define mp(x,y) make_pair((x),(y)) #define INF 0x3f3f3f3f using namespace std; typedef long long ll; typedef vector<long long> vl; typedef long double ld; struct LongPipes { int fewestWelds(vector <string> segments, string desiredLength) { vl s; ll d; each(i, segments) { stringstream ss(*i); ld x; ss >> x; s.pb((ll)(x * 1000 + .5)); } { stringstream ss(desiredLength); ld x; ss >> x; d = (ll)(x * 1000 + .5); } int n = s.size(); int m = n/2; static pair<ll,int> q[1<<19]; int qn = 0; //まず半分の組み合わせを全部列挙して計算して rep(i, 1<<m) { ll w = 0; int p = 0; rep(j, m) if((i >> j)&1) w += s[j], p ++; q[qn++] = mp(w, -p); } //(Length, -Count) となっている //Lengthでソート sort(q, q+qn); //同じLengthなのにCountが違うやつは後の二分探索時に面倒なので、 //ここでまとめて、同じLengthならCountを一番小さいやつに合わせる //ソート時の順序・forの順番に注意。ここでミスってFailedした //マイナス付けずにforを前からやってminでも同じだしそのほうが簡潔だね。普通のナップサックではvalueが大きいほうがいいからそれに合ってるだけ for(int i = qn-1; i > 0; i --) { if(q[i-1].first == q[i].first) { q[i-1].second = max(q[i-1].second, q[i].second); } } int r = INF; //残り半分を全列挙 rep(i, 1<<(n-m)) { ll w = 0; int p = 0; rep(j, n-m) if((i >> j)&1) w += s[m+j], p ++; //二分探索なので、範囲にないなら最初にcontinueしておく //だけれども、今回はExactで最後に判定するので、これは必要なかったかな if(q[qn-1].first + w < d) continue; int l = 0, u = qn; //ちょうど長さが(d - w)になる、最初の半分の組み合わせを二分探索 while(u - l > 1) { int mid = (l + u) / 2; if(q[mid].first + w <= d) l = mid; else u = mid; } //ちゃんと合ってるか?(見つけられたか?) if(q[l].first + w == d) { r = min(r, p + -q[l].second); } } return r == INF ? -1 : r-1; } };