codeJan与青蛙

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述 

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求出最少的WA声,dp[i][j]表示第i个位置放了一个黑洞,并且第i个位置以右一共放了j个黑洞听到的最少的WA声。对于求最少的黑洞数,可以二分黑洞数,然后同样的dp去验证,只是需要加个数量的判断

#include <iostream>  
#include <cstdio>  
#include <cstring>  
#include <string>  
#include <algorithm>  
#include <cmath>  
#include <map>  
#include <set>  
#include <stack>  
#include <queue>  
#include <vector>  
#include <bitset>  
#include <functional>  

using namespace std;

#define LL long long  
const LL INF = 0x3f3f3f3f3f3f3f3f;

int n, m;
LL sum1[109][109], sum2[109][109], dp[109][109];
LL ans1, ans2;
struct node
{
	int a, b;
	bool operator<(const node &x)const
	{
		return a < x.a;
	}
}x[109];

int check(LL mid)
{
	memset(dp, INF, sizeof dp);
	for (int i = 1; i <= n; i++)
		if (sum2[i][n] <= mid) dp[i][1] = sum1[i][n];
	for (int i = 2; i <= m; i++)
		for (int j = n - i + 1; j >= 1; j--)
			for (int k = j + 1; k + i - 2 <= n; k++)
				if (sum2[j][k - 1] <= mid) dp[j][i] = min(dp[j][i], dp[k][i - 1] + sum1[j][k - 1]);
	if (ans1 == dp[1][m]) return 1;
	return 0;
}

int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d%d", &n, &m);
		ans2 = 0;
		for (int i = 1; i <= n; i++) scanf("%d%d", &x[i].a, &x[i].b), ans2 += x[i].b;
		sort(x + 1, x + 1 + n);
		for (int i = 1; i <= n; i++)
		{
			for (int j = i; j <= n; j++)
			{
				sum1[i][j] = sum1[i][j - 1] + 1LL * (x[j].a - x[i].a)*x[j].b;
				sum2[i][j] = sum2[i][j - 1] + x[j].b;
			}
		}
		memset(dp, INF, sizeof dp);
		for (int i = 1; i <= n; i++) dp[i][1] = sum1[i][n];
		for (int i = 2; i <= m; i++)
			for (int j = n - i + 1; j >= 1; j--)
				for (int k = j + 1; k + i - 2 <= n; k++)
					dp[j][i] = min(dp[j][i], dp[k][i - 1] + sum1[j][k - 1]);
		ans1 = dp[1][m];
		LL l = 1, r = ans2;
		while (l <= r)
		{
			LL mid = (l + r) >> 1;
			if (check(mid)) { ans2 = mid, r = mid - 1; }
			else l = mid + 1;
		}
		printf("%lld %lld\n", ans1, ans2);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值