小韦老师@神犇营-USACO1.3.2-修理牛棚

小韦老师@神犇营-USACO1.3.2-修理牛棚

题目:

描述

题意:
在一个夜黑风高,下着暴风雨的夜晚,农民约翰的牛棚的屋顶、门被吹飞了。 好在许多牛正在度假,所以牛棚没有住满。 这些牛在互相挨着的排成一排的牛棚中过夜。 有些牛棚里有牛,有些没有。 所有的牛棚有相同的宽度。 自门遗失以后,农民约翰必须尽快在牛棚之前竖立起新的木板。 他的新木材供应商将会供应他任何他想要的长度,但是供应商只能提供有限数目的木板。 农民约翰想将他购买的木板总长度减到最少。
给出:可能买到的木板最大的数目 M (1 <= M <= 50);牛棚的总数 S (1 <= S <= 200); 牛棚里牛的总数 C (1 <= C <= S); 和牛所在的牛棚的编号 stall_number (1 <= stall_number <= S), 计算拦住所有有牛的牛棚所需木板的最小总长度。
输出所需木板的最小总长度作为答案。
注意:
牛棚的长度为被拦住的牛棚总数量。

输入

第 1 行: M , S 和 C (每两个数之间用一个空格分开) 。
第 2 到 C+1行: 每行包含一个整数,表示牛所占的牛棚的编号。

输出

单独的一行包含一个整数表示被拦住的牛棚总数量。

输入样例1

4 50 18
3
4
6
8
14
15
16
17
21
25
26
27
30
31
40
41
42
43

输出样例1

25

题解:

破题:

总共有 M 块木板,有 C 头牛,在把所有牛都拦住的前提下,求被拦住的牛棚总数量的最小值。

思路:

方法一:贪心算法
用变量 ans 表示拦住的牛棚数量的最小值,并且将 ans 初始化为 0。
(1)计算有牛的牛棚连通块的块数 num,若 num ≤ M,则转到(2),否则转到(3)。
(2)num ≤ M 说明木板数比牛的数量多,则 ans = C,因为至少要保证每头牛都要被拦住。
(3)如果 M < C,则首先要将牛所在的牛棚号进行升序排列,求出每一个间隔的大小,存在一个数组 mid 中,将间隔的数组进行升序排列,初始化 index 为 0,转到(4)。
(4)如果 num > M,则循环执行 ans += mid[index++],num–,否则结束循环。
(5)输出 ans 即为所求。
方法二:动态规划
用数组 stall 来存储有牛的牛棚编号,需要升序排列。
用 f[i][j] 来作为描述信息,表示前 i 块木板挡住前 j 个有牛的牛棚的最小长度(这里注意是有牛棚的牛棚,j 不是牛棚编号,而是 stall 数组的编号)。
(1)当 j = 1 时,f[i][j] = 1
【注】
当 j = 1,说明只有一个牛棚里有牛,则只需要挡住这一个有牛的牛棚就可以,所以最小长度为 1。
(2)当 i = 1 时,f[i][j] = stall[j] - stall[1] + 1
【注】
当 i = 1 时,说明只有一块木板,则需要挡住的最小长度是从第 1 个有牛的牛棚到第 j 个有牛的牛棚。
(3)当 i != 1 && j != 1 时,f[i][j] = min(f[i-1][j-1] + 1, f[i][j - 1] + stall[j] - stall[j - 1])
【注】
f[i - 1][j - 1] 表示的是前 i - 1 个木板挡住了前 j - 1 个有牛的牛棚的最小长度,所以第 i 块木板就给第 j 个有牛的牛棚,所以加一即可。
f[i][j-1] 表示前 i 个木板挡住了前 j - 1 个有牛的牛棚的最小总长度,现在已经没有木板了,只能把挡住第 j - 1 的那个木板延伸到第 j 个有牛的牛棚,所以增加的长度为 stall[j] - stall[j - 1]

完整代码:

方法一:贪心算法

#include<bits/stdc++.h>

using namespace std;

const int N = 210;
int m;  // 最大木板数
int s;  // 牛棚总数
int c;  // 牛的总数
int stall[N];  // 有牛的牛棚编号
int mid[N];  // 有牛的牛棚之间的间隔

int block() {
	int num = 1;  // 记得要初始化为 1
	for (int i = 0; i < c - 1; i++) {
		if (stall[i + 1] != stall[i] + 1) {
			num++;
		}
	}
	return num;  // 记得返回
}

void cal_mid() {
	int index = 0;  // 数组下标
	for (int i = 1; i < c; i++) {
		// m 为间隔
		int m = stall[i] - stall[i - 1] - 1;
		// 若有间隔,则将间隔存到数组 mid 中
		if (m) mid[index++] = m;
	}
	sort(mid, mid + index);  // 记得升序排序
}

int main() {
    
	cin >> m >> s >> c;
	for (int i = 0;  i < c; i++) {
		cin >> stall[i];
	}
	sort(stall, stall + c);
	int num = block();
	int ans = 0;  // 被拦住的牛棚总数量的最小值
	if (num <= m) ans = c;
	else {
		cal_mid();
		int index = 0;  // 数组下标
		while (num > m) {
			ans += mid[index++];  // 依次加上间隔数
			num--;  // 连通的块数减一
		}
		ans += c;
	}
	cout << ans;
	
	return 0;
}

方法二:动态规划

#include <bits/stdc++.h>

using namespace std;

const int N = 210;
int m;  // 最大木板数
int s;  // 牛棚总数
int c;  // 牛的总数
int stall[N];  // 有牛的牛棚编号
int f[N][N];

int main() {
    
	cin >> m >> s >> c;
	for (int i = 1;  i <= c; i++) {
		cin >> stall[i];
	}
	// 将有牛的牛棚升序排列,记得要加 1
	sort(stall + 1, stall + c + 1);
	for (int i = 1; i <= m; i++) {
		for (int j = 1; j <= c; j++) {
			if (j == 1) {  // 只有一个牛棚有牛
				f[i][j] = 1;
			} else if (i == 1) {  // 只有一块木板
				f[i][j] = stall[j] - stall[1] + 1;
			} else {
				f[i][j] = min(f[i - 1][j - 1] + 1, f[i][j - 1] + stall[j] - stall[j - 1]);
			}
		}
	}
	// 输出结果
	cout << f[m][c];
	
	return 0;
}            

猜你喜欢

转载自blog.csdn.net/qq_31790997/article/details/92082932