Wannafly挑战赛7 D - codeJan与青蛙
codeJan喜欢观察世界。有一天,codeJan发现一个非常奇怪的现象。有一些年轻的青蛙聚集在一条直线上的某些位置上,同一个位置可能有多个青蛙。这些青蛙每次只会向前跳一米,并且每只青蛙每跳一次都会发出’WA’的一声。codeJan想在一些青蛙的位置上放置黑洞来收集全部的青蛙。在黑洞位置上的青蛙会直接掉进黑洞中不会发出任何叫声,其余的青蛙经过若干次跳跃都会掉进在它前面的最近的黑洞。因为WA类似WrongAnswer,所以codeJan想要知道他合理地安排黑洞的位置,最少可以听到多少声WA?在听到最少声WA时,需要准备的每个黑洞的容量至少为多少?黑洞的容量体现为可以容纳的青蛙的最大数量,所有黑洞的容量应该都相同。
输入描述:
第一行一个正整数T≤20表示测试组数。每组测试样例的第一行是两个正整数n,m,分别表示存在青蛙的位置和可以放置黑洞的数量。接下来n行,每行包含两个正整数a[i],b[i]分别表示第i组青蛙的位置(单位:米)和这个位置上青蛙的数量。
输出描述:
对于每组测试用例用一行输出两个正整数分别表示最少可以听到多少声WA和黑洞的最少容量。用空格隔开,行末无空格。
示例1
输入
2 3 1 1 1 2 2 3 3 3 2 1 1 4 2 2 3
输出
8 6 3 4
说明
对于第一个样例,只能放在 1 位置,因此听到的 WA 的次数为:1*0+2*1+3*2=8,所有青蛙汇聚在一次,容量至少为 6。
对于第二个样例,可以放在 1 和 4 位置,因此听到的 WA 的次数为:1*0+3*1+2*0=3,1 位置和 2 位置的青蛙汇聚在一 起,需要容量为 4;4 位置的青蛙单独在一起,需要容量为 2。因此容量至少为 4
备注:
输入保证a[i] ≠a[j](i ≠j),1≤m≤n≤100,1≤a[i],b[i]≤106。
题解
$dp$,二分。
$dp[i][j]$表示到$i$位置,分了$j$段的最小花费,每一个区间的花费可以预处理。
$dp[1][m]$就是第一个答案。
对于第二个答案,容量越大,最小花费肯定越小,容量越小,最小花费越大,利用这个单调性就可以二分,假设二分到的容量为$x$,那么$dp$的时候外加一个条件控制一下区间和要小于等于$x$。
#include <bits/stdc++.h> using namespace std; const int maxn = 100 + 10; int T, n, m; struct X { int a, b; }s[maxn]; long long cost[maxn][maxn]; long long dp[maxn][maxn]; long long sum[maxn][maxn]; long long limit; bool cmp(const X& a, const X& b) { return a.a < b.a; } int check(long long mid) { for(int i = 1; i <= n; i ++) { dp[i][1] = 1e18; if(sum[i][n] <= mid) dp[i][1] = cost[i][n]; } for(int j = 2; j <= m; j ++) { for(int i = n - j + 1; i >= 1; i --) { dp[i][j] = 1e18; for(int k = i + 1; k <= n; k ++) { if(dp[k][j - 1] != 1e18 && sum[i][k - 1] <= mid) { dp[i][j] = min(dp[i][j], dp[k][j - 1] + cost[i][k - 1]); } } } } if(limit == dp[1][m]) return 1; return 0; } int main() { scanf("%d", &T); while(T --) { scanf("%d%d", &n, &m); for(int i = 1; i <= n; i ++) { scanf("%d%d", &s[i].a, &s[i].b); } sort(s + 1, s + 1 + n, cmp); for(int i = 1; i <= n; i ++) { for(int j = i; j <= n; j ++) { cost[i][j] = cost[i][j - 1] + 1LL * (s[j].a - s[i].a) * s[j].b; sum[i][j] = sum[i][j - 1] + s[j].b; } } for(int i = 1; i <= n; i ++) dp[i][1] = cost[i][n]; for(int j = 2; j <= m; j ++) { for(int i = n - j + 1; i >= 1; i --) { dp[i][j] = 1e18; for(int k = i + 1; k <= n; k ++) { dp[i][j] = min(dp[i][j], dp[k][j - 1] + cost[i][k - 1]); } } } limit = dp[1][m]; long long ans2 = sum[1][n]; long long L = 1, R = ans2; while(L <= R) { long long mid = (L + R) / 2; if(check(mid)) ans2 = mid, R = mid - 1; else L = mid + 1; } printf("%lld %lld\n", limit, ans2); } return 0; }