P4267 [USACO18FEB]Taming the Herd

说实话感觉不是一道蓝题……感觉挺水的,不过为了水题解,水题就够了(其实是觉得思考的过程比较典型,记录一下)

题解

刚开始看这道题感觉上没什么思路,但是我们可以先考虑用 \(O(n)\) 的时间去枚举发生的出逃次数,再用 \(O(n^2)\) 的时间去计算每一个出逃次数的情况下不一致条目的最小值。

现在我们考虑对于任意一个出逃次数 \(d\) ,我们如何计算。不妨设 \(f_{i,j}\) 表示到第 \(i\) 个点出逃过 \(j\) 次的最小差异值,易得 \(dp\) 方程为:

\[f_{i,j}=min(f_{k,j-1}+cost_{k+1,i}) \]

其中 \(cost_{l,r}\) 是指:区间 \(l\) ~ \(r\) 为一次完整的出逃区间(即其中没有发生过一次出逃且 \(l\)\(r+1\) 发生了出逃)时的差异值。可以发现这个东西是可以 \(O(n^2)\) 预处理的。

那么现在需要枚举 \(i\)\(j\)\(k\)\(d\),复杂度为 \(O(n^4)\) ,肯定是不行的,但是我们可以发现在处理略大的 \(d\) 值时其实是可以计算出较小的 \(d\) 值的,所以我们可以直接一起计算,就不需要枚举 \(d\) 了,复杂度就降为 \(O(n^3)\) ,可行了。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,a[N];
int cost[N][N],f[N][N];
int main()
{
	cin>>n;
	for(int i=1;i<=n;++i)
	scanf("%d",&a[i]);
	for(int i=1;i<=n;++i)
	{
		for(int j=i;j<=n;++j)
		cost[i][j]=cost[i][j-1]+(a[j]!=j-i);
	}
	// for(int i=1;i<=n;++i)
	// {
	// 	for(int j=i;j<=n;++j)
	// 	printf("%d %d %d\n",i,j,cost[i][j]);
	// }
	memset(f,63,sizeof(f));
	for(int i=1;i<=n;++i)
	{
		f[i][1]=cost[1][i];
		for(int j=1;j<i;++j)
		{
			for(int k=1;k<=j;++k)
			f[i][k+1]=min(f[i][k+1],f[j][k]+cost[j+1][i]);
		}
	}
	for(int i=1;i<=n;++i)
	printf("%d\n",f[n][i]);
	return 0;
}
posted @ 2020-09-04 20:54  Point_King  阅读(100)  评论(0编辑  收藏  举报