题意:有一个长度为L的环,环上一些点有苹果,每次最多能拿k个苹果,问把全部苹果都拿完所走过的最短距离。
思路:贪心加dp。
首先,如果这个环从中点处割开,那么只需要对两边进行贪心即可,现在连在一起,那么多了一种可能,即绕环一周拿苹果这种方案。
不难证明,至多只可能绕环一次,因为绕环多次可以分为对一侧拿苹果加绕一圈这种方案。
具体实现:将cnt棵苹果树分为n棵每棵上有一个苹果的苹果树,记录每个苹果的位置。
对于两侧,记录拿m个苹果所需要的最小距离,这一步可以用dp实现,即sul[m] = sul[m-k]+posl[m]。
最后讨论绕圈的问题,绕圈所拿的k个苹果一定要从靠近中点处的苹果中取,即
ans = min(ans, sul[nl-i]+sur[max(nr-K+i, 0)]+L)。
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #include<map> #include<set> #define eps 1e-6 #define LL long long using namespace std; const int maxn = 100000 + 50; const int INF = 0x3f3f3f3f; int L, cnt, K, n, nl, nr; LL sul[maxn], sur[maxn]; int pos[maxn], posl[maxn], posr[maxn]; int main() { // freopen("input.txt", "r", stdin); int t; cin >> t; while(t--) { n = nl = nr = 0; scanf("%d%d%d", &L, &cnt, &K); while(cnt--) { int x, a; scanf("%d%d", &x, &a); while(a--) pos[++n] = x; } for(int i = 1; i <= n; i++) if(2*pos[i] < L) posl[++nl] = pos[i]; else posr[++nr] = L - pos[i]; sort(posl+1, posl+1+nl); sort(posr+1, posr+nr+1); for(int i = 1; i <= nl; i++) if(i > K) sul[i] = sul[i-K] + posl[i]*2; else sul[i] = posl[i]*2; for(int i = 1; i <= nr; i++) if(i > K) sur[i] = sur[i-K] + posr[i]*2; else sur[i] = posr[i]*2; LL ans = sul[nl] + sur[nr]; for(int i = 1; i<=nl && i<K; i++) ans = min(ans, sul[nl-i]+sur[max(nr-K+i, 0)]+L); cout << ans << endl; } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-07 09:28:49