[USACO1.3]修理牛棚 Barn Repair

版权声明:转载无所谓的。。。 https://blog.csdn.net/xuxiayang/article/details/82953137

大意

给定 n n 头牛所在的一些位置,现在要用不大于 m m 块连续的木板把每头牛拦住,问最少需要的木板总长度


d p dp 思路

动态规划
f [ i ] [ j ] f[i][j] 表示前 i i 个牛,用了 j j 个木板的最小木板总长度。

分两种情况

  1. 我木板够,则 f [ i ] [ j ] = f [ i 1 ] [ j 1 ] + 1 f[i][j]=f[i-1][j-1]+1
  2. 我木板不够,则 f [ i ] [ j ] = f [ i 1 ] [ j ] + a [ i ] a [ i 1 ] f[i][j]=f[i-1][j]+a[i]-a[i-1]

因为我们要使总长度尽量短,所以 a [ i ] a [ i 1 ] a[i]-a[i-1] 也要尽量小,那么我们就可以排序后处理即可

时间复杂度为: O ( n l o g n + n m ) O(nlogn+nm)
空间复杂度为: O ( n m ) O(nm)


然后我们发现, f [ i ] f[i] 只跟 f [ i 1 ] f[i-1] 有关,所以我们可以用滚动数组优化,空间复杂度降低到了: O ( 2 m ) O(2m)


其实还有一种优化,就是如果我们把 j j 倒过来循环的话,那么 f [ j 1 ] f[j-1] 就必然是上一次的,这样空间复杂度就进一步降低到了: O ( m ) O(m)


d p dp 代码

/*
ID:hzbismy1
LANG:C++
TASK:barn1
*/
#include<cstdio>
#include<algorithm>
using namespace std;int f[51],n,m,s,a[201];
signed main()
{
	freopen("barn1.in","r",stdin);
	freopen("barn1.out","w",stdout);
	scanf("%d%d%d",&m,&s,&n);
	if(m>=n)return printf("%d",n)&0;//特判
	for(register int i=1;i<=n;i++) scanf("%d",a+i);
	sort(a+1,a+1+n);//排序减小差值
	for(register int i=1;i<=n;f[0]=502345678,i++)//记得把f[0]赋值为无穷大
	for(register int j=m;j>0;j--) f[j]=min(f[j-1]+1,f[j]+a[i]-a[i-1]);//动态转移
	printf("%d",f[m]);//输出
}

贪心思路

首先我们假设一开始有一块长度为 m m 的木板拦住了所有的牛,然后断开 m 1 m-1 处,既然要使木板的总长度尽量小,那我们断开的木板的长度一定要尽量长,我们先算出每头牛它左边(或右边)与它最近的牛的长度,然后排序,依次减去长度更长的,就一定满足是最优的(因为只会断开那些无用的地方,所以答案是可行的)


代码

#include<cstdio>
#include<algorithm>
using namespace std;int n,m,s,a[201],cz[201],ans;
signed main()
{
	scanf("%d%d%d",&m,&s,&n);
	if(m>=n)return printf("%d",n)&0;
	for(register int i=1;i<=n;i++) scanf("%d",a+i);
	sort(a+1,a+1+n);
	ans=a[n]-a[1]+1;//一开始一个木板拦住了所有的牛
	for(register int i=2;i<=n;i++) cz[i-1]=a[i]-a[i-1];//计算差值
	sort(cz+1,cz+n);//排序
	for(register int i=n-1,j=1;j<m;j++,i--) ans=ans-cz[i]+1;//减去差值最大的那几个段
	printf("%d",ans);//输出
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/82953137
今日推荐