usaco 月赛 2008 February Eating Together 题解

【题目】

Eating Together
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 5172 Accepted: 2524

Description

The cows are so very silly about their dinner partners. They have organized themselves into three groups (conveniently numbered 1, 2, and 3) that insist upon dining together. The trouble starts when they line up at the barn to enter the feeding area.

Each cow i carries with her a small card upon which is engraved Di (1 ≤ Di ≤ 3) indicating her dining group membership. The entire set of N (1 ≤ N ≤ 30,000) cows has lined up for dinner but it's easy for anyone to see that they are not grouped by their dinner-partner cards.

FJ's job is not so difficult. He just walks down the line of cows changing their dinner partner assignment by marking out the old number and writing in a new one. By doing so, he creates groups of cows like 111222333 or 333222111 where the cows' dining groups are sorted in either ascending or descending order by their dinner cards.

FJ is just as lazy as the next fellow. He's curious: what is the absolute mminimum number of cards he must change to create a proper grouping of dining partners? He must only change card numbers and must not rearrange the cows standing in line.

Input

* Line 1: A single integer: N
* Lines 2..N+1: Line i describes the i-th cow's current dining group with a single integer: Di

Output

* Line 1: A single integer representing the minimum number of changes that must be made so that the final sequence of cows is sorted in either ascending or descending order

Sample Input

5
1
3
2
1
1

Sample Output

1

Source


【序言】本来像这种水题是不发题解的。但是我的DP比较薄弱,而且也好长时间没有训练了,因此写一下来加深印象。

【大意】给出N头奶牛的编号(1~3),每次修改一头奶牛编号代价是1,求最小的代价使得N头奶牛呈递增或递减。

【分析1】这道题奶牛编号的范围放的比较小。有一种方法可以直接求代价,而且没有编号限制。设S为代价,则可得Smin=(N-K),其中N是元素个数,K是这段数中最长的不下降(或不上升)序列的元素个数。

【分析1的证明】我们设在N个数中改变其中的S个数使其规范。假设把这S个数全部去掉,剩下的数必然呈不下降(或不上升)状态。我们想让S尽量的小,就是要使得(N-S)尽量的大。那么,当(N-S)最大的时候,S就是最小的。

【分析2】因为N<=30000,按照分析1的做法求最长不下降(不上升)序列,要用到N(log(N))的方法,这样有点麻烦。根据数据小的特点,我们可以做一个DP。

设f[i][j]是到第i个点,把其修改成j的最小代价。

如果j=a[i](即不用改了),f[i][j]=min(f[i-1][j],f[i-1][j-1])……

否则我们就要用1的代价来修改,f[i][j]=f[i-1][j]+1

【注意】因为题面中上升或下降都可以,所以要做两遍。

【AC代码】

#include<stdio.h>
#include<algorithm>
#include<cstring>
const int INF=999999;
int f[30005][4],a[30005],ans,t,i,n;
using namespace std;
void get()
{
  for (int i=1;i<=n;i++)
  {
    for (int j=1;j<=3;j++)
      if (a[i]==j)
      {
        f[i][j]=INF;
        for (int k=j;k>0;k--)
          f[i][j]=min(f[i-1][k],f[i][j]);
      }
      else f[i][j]=f[i-1][j]+1;
  }
}
int main()
{
  scanf("%ld",&n);
  for (i=1;i<=n;i++)
    scanf("%ld",&a[i]);
  get();
  ans=min(f[n][1],f[n][2]);
  ans=min(ans,f[n][3]);
  memset(f,0,sizeof(f));
  for (i=1;i<=n/2;i++) {t=a[i];a[i]=a[n-i+1];a[n-i+1]=t;}
  get();
  ans=min(f[n][1],ans);
  ans=min(f[n][2],ans);
  ans=min(f[n][3],ans);
  printf("%ld",ans);
  return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值