BZOJ1609 [Usaco2008 Feb]Eating Together麻烦的聚餐

题目大意:把一个只含1,2,3的序列改成形如111……222……333……或333……222……111……的形式最少改几个数。

题解:光看这个数列无从知晓答案,所以试着采用DP。由于每个数变1,2,3与后面的数怎么变密切相关,所以F[i][j]表示前i个数中,第i个数变j后满足第一种形态的最少次数,则有F[i][j]=F[i-1][k]+diff(a[i],j),其中k∈[1,j],diff(a,b)表示a,b是否不同。形态2的话,既可以把上式的“i-1”改为“i+1”,又可以把整个序列前后颠倒再搞一次,非常自由。

代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cctype>
using namespace std;

int qread()
{
    char c;int s=0;
    while (!isdigit(c=getchar()));
    do {s=s*10+c-'0';} while (isdigit(c=getchar()));
    return s;
}

int n;
#define maxn 30233
int a[maxn],f[maxn][5];
#define inf 0x7fffffff
int dif(int x,int y)
{
    return (x==y?0:1);
}
void solve()
{
    for (int j=1;j<=3;j++) f[0][j]=0;
    for (int i=1;i<=n;i++)
        for (int j=1;j<=3;j++)
        {
            f[i][j]=inf;
            int d=dif(a[i],j);
            for (int k=j;k;k--) f[i][j]=min(f[i][j],f[i-1][k]+d);
        }
}
int main()
{
    n=qread();
    for (int i=1;i<=n;i++) a[i]=qread();
    solve();
    int ans1=min(f[n][1],min(f[n][2],f[n][3]));
    for (int i=1;i<=n/2;i++) {int t=a[i];a[i]=a[n-i+1];a[n-i+1]=t;}
    solve();
    int ans2=min(f[n][1],min(f[n][2],f[n][3]));
    printf("%d\n",min(ans1,ans2));
    return 0;
}
View Code

 

posted @ 2016-12-11 10:38  Blue233333  阅读(270)  评论(0编辑  收藏  举报