磁盘碎片整理
时限: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; }