https://www.luogu.org/problemnew/show/P4267

题意:一群奶牛在若干天出逃,农夫从出逃的那天开始记录在本子上记录,每次记录的信息是:最近的上一次出逃是几天前,当天就是0天前。但是有一些信息被篡改了。求:如果在这n天出逃了x次,有可能被篡改天数的最小值。x∈[1,n]都要求。

思路:

一开始自己尝试了设f(i,j):前i天出逃j次对应的最少篡改次数,那么第i天由前i-1天转移而来,要么第i天新出逃,也就是应该记录0,要么应该接着上一天记录数字+1,也就是f(i,j)=min{f(i-1,j-1)+(a[i]!=0),f(i-1,j)+(a[i]!=a[i-1]+1)},但是a[i-1]未必是实际应该记录的值,它也有可能被篡改了。例如样例:1,1,2,0,0,1。f(1,1)=1,本来应该f(2,1)也是1,但是误以为1处实际是1,就搞错了。

后来又改成f(i,j,0/1)+last记录上一个位置实际应该记录的,也失败了。

最后改成设f(i,j,k):前i天出逃j次并且第i天实际应该填k对应的最少篡改次数。

那么f(i,j,0)=min{f(i-1,j-1,x)},x∈[0,n],f(i,j,k)=f(i-1,j,k-1)+(a[i]!=k),过了。

#include<bits/stdc++.h>
using namespace std;
#define maxn 110

int n,a[maxn];

int f[maxn][maxn][maxn];

void debug()
{
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)printf("f[%d][%d][0]=%d,[1]=%d\n",i,j,f[i][j][0],f[i][j][1]);
	}
}

int main()
{
//	freopen("input.in","r",stdin);
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=0;i<=n;i++)for(int j=0;j<=n;j++)for(int k=0;k<=n;k++)f[i][j][k]=(1<<25);
	f[0][0][0]=0;f[1][1][0]=(a[1]!=0);
	for(int i=2;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{	
			int minn=(1<<25);for(int k2=0;k2<=n;k2++)minn=min(minn,f[i-1][j-1][k2]);
			f[i][j][0]=minn+(a[i]!=0);
			for(int k=1;k<=n;k++)
			{
				f[i][j][k]=f[i-1][j][k-1]+(a[i]!=k);
			}
		}
	}
	for(int j=1;j<=n;j++)
	{
		int minn=(1<<25);
		for(int k2=0;k2<=n;k2++)minn=min(minn,f[n][j][k2]);
		cout<<minn<<endl;
	}
//	debug();
	return 0;
}