题解 P5124 【[USACO18DEC]Teamwork G】
linyinuo2008 · · 题解
原题链接
这是一道非常棒的线性动归题,其中还有一些操作小技巧。
1、题意理解
简化版题意:给你
例:
5 3
1
2
4
6
1
我们把
通过这个例子,应该已经理解了题意,下面就是一些要点。
-
我们要分的是这
n 头奶牛,并且每组必须是连续的不超过k 头奶牛。 -
一只奶牛只能属于一组,可以理解为在奶牛序列中放上几块隔板。
2、动态规划求解答案
这题一看就是动态规划,并且符合无后效性的性质(标签上不是写着呢吗)。这里我摘录一段来自这篇博客的解释:
无后效性,有两层含义,第一层含义是,在推导后面阶段状态的时候,我们只关心前面阶段的状态值,不关心这个状态是怎么一步步推导出来的。第二层含义是,某阶段状态一旦确定,就不受之后阶段的决策影响。无后效性是一个非常“宽松”的要求。
所以动归状态很自然的就出来了。我们设
来个栗子。
如图,当
新进来了一头红牛,编号为
由于一组最多只能放三头,所以我们有三种方法:
把它单独放一组,这种方案是从
把它和前一只奶牛放一组,这种状态是从
把它和前两只奶牛放一组,这种状态是从
至此,我们就完成了这只奶牛的全部状态转移,并且无法再找到新的状态,因为再往前分组就会超出
所以我们的最终
但是
于是我们就有了这样的代码:
#include <iostream>
using namespace std;
const int N=10005;
int n,k,s[N];
int f[N];
inline int max(int a,int b)
{
return a>b?a:b;
}
int mx(int k,int l)//在[k,l]区间内取最大值
{
int maxx=-1;
for(int i=k;i<=l;i++) maxx=max(maxx,s[i]);
return maxx;
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>s[i];
f[1]=s[1];
for(int i=2;i<=n;i++)
{
for(int j=max(i-k+1,1);j<=i;j++)
{
f[i]=max(f[i],f[j-1]+mx(j,i)*(i-j+1));//转移
}
}
cout<<f[n];
return 0;
}
交上去一看,TLE
这是为什么呢?是在这里出了问题:
int mx(int k,int l)//在[k,l]区间内取最大值
{
int maxx=-1;
for(int i=k;i<=l;i++) maxx=max(maxx,s[i]);
return maxx;
}
这里求区间最大值的时间是
3、AC代码
代码中有注释。
#include <iostream>
using namespace std;
const int N=10005;
int n,k,s[N];
int f[N];
/*
f[i]表示前i头奶牛最大水平和
f[i]=max(f[i],f[j-1]+mx(j,i)*(i-j+1)) (i-k+1<=j<=i)
*/
int max(int a,int b)
{
return a>b?a:b;
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>s[i];
f[1]=s[1];//初始化f[1]=s[1]
for(int i=2;i<=n;i++)
{
int mx=-1;//先设为-1,因为都是正数
for(int j=i;j>=max(i-k+1,1);j--)//边界条件
{
mx=max(mx,s[j]);//先得把mx更新了
f[i]=max(f[i],f[j-1]+mx*(i-j+1));//才能转移
}
}
cout<<f[n];
return 0;
}
若有错误,欢迎指出!