fzu_noip 1036(磁盘碎片整理-Dp)

磁盘碎片整理

时限:1s 内存:32M

★问题描述:

Jack最近在PS海报。海报所需各种素材不但让Jack头大,也让硬盘分区中的文件碎片越来越多,电脑的反应速度越来越慢。

烦恼的Jack决定好好整理一下磁盘的碎片。但是,为了体现高手风范,自负的Jack不喜欢系统自带的整理工具,决定自己编写一个,希望你帮他完成简单的第一遍整理。

因为不可移动的系统文件的分隔,Jack的N个文件,分为许多文件块,杂乱分布在M个长度不等的区间中。在第一遍整理时,只将每个区间中的碎片进行顺序改变,不跨区间移动。

因为在Jack完成的第二次整理时,将把M个区间依顺序连接成一个完整区间,希望整理完成后,零碎度越低越好。(若连接后的完整区间,文件块i与文件块i+1属于不同文件,则零碎度+1)。

例:

N=5 M=3

存储区间1:长度3 文件块[4,3,1]

存储区间2:长度4 文件块[2,1,4,4]

存储区间3:长度3 文件块[1,3,1]

第一次整理,第二次连接后:[1,3,4][4,4,2,1][1,1,3],零碎度为5

★数据输入:

第一行n m(1<=n<=10,1<=m<=10000),表示有n个文件,m个存储区间。

接下来m行,每行第一个整数为Li(0<Li<=50),表示存储区间i的长度。接下来Li个整数,分别表示该区间第j文件块所属文件。

★结果输出:

输出两次整理完成后,能达到的最小零碎度。

输入示例

输出示例

5 3

3 4 3 1

4 2 1 4 4

3 1 3 1

5

 

 

简单Dp,f[i][j]表示到第i块硬盘前面接j能取得的最大头尾连接数(开始那个默认与之前的连接),则


F[i][j]=| max(f[i][l[j]],f[i-1][k]+(b[i-1][l[j]]&&(k!=l[j]||pre_size==1))) (i>1)

          |1 (i=1)

答案为tot-max(f[m][k](0<=k<n)) tot为每个区间不同数字类数的总和

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<functional>
#include<cmath>
using namespace std;
#define MAXN (10)
#define MAXM (10000+10)
#define MAXLi (50)
int n,m;
bool b[MAXM][11];
int f[MAXM][11];
int l[MAXN];
int main()
{
  //  freopen("piece.in","r",stdin);
    memset(b,0,sizeof(b));
    memset(f,0,sizeof(f));
    scanf("%d%d",&n,&m);
    int ans=0,pre_size=0;
    for (int i=1;i<=m;i++)
    {
        int li;
        scanf("%d",&li);
        for (int j=1;j<=li;j++) scanf("%d",&l[j]);
        sort(l+1,l+li+1);
        int size=unique(l+1,l+li+1)-(l+1);
        ans+=size;
    //  cout<<size<<endl;
    //  for (int i=1;i<=size;i++) cout<<l[i]<<' ';cout<<endl;
        for (int j=1;j<=size;j++)
            b[i][l[j]]=1;
        if (i==1)
        {
           for (int j=1;j<=size;j++)
               f[1][l[j]]=1;
        }
        else
        {
        for (int j=1;j<=size;j++)
            for (int k=0;k<n;k++)
                if (b[i-1][k])
                {
                   f[i][l[j]]=max(f[i][l[j]],f[i-1][k]+(b[i-1][l[j]]&&(k!=l[j]||pre_size==1)));
                }
        }
        pre_size=size;
    }
    int p=0;
    for (int j=0;j<n;j++) {p=max(p,f[m][j]); }
/*
    for (int i=1;i<=m;i++)
    {
        for (int j=0;j<n;j++)
            cout<<f[i][j]<<' ';
        cout<<endl;
    }
*/
    cout<<ans-p<<endl;
//   while (1);
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值