1. 程式人生 > 實用技巧 >題解 P4267 【[USACO18FEB]Taming the Herd】

題解 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;
}