【UVA1289】Stacking Plates

来源:互联网 发布:关系型数据库基本特征 编辑:程序博客网 时间:2024/06/05 06:29

Description

The Plate Shipping Company is an Internet retailer that, as their name suggests, exclusively sells plates. They pride themselves in offering the widest selection of dinner plates in the universe from a large number of manufacturers.

In a recent cost analysis the company has discovered that they spend a large amount of money on packing the plates for shipment. Part of the reason is that plates have to be stacked before being put into shipping containers. And apparently, this is taking more time than expected. Maybe you can help.

A shipment of plates consists of plates from several manufacturers. The plates from each manufacturer come stacked, that is, each arranged in a single stack with plates ordered by size (the smallest at the top, the largest at the bottom). We will call such a stackproperly ordered. To ship all these plates, you must combine them into a single stack, again properly ordered. To join the manufacturers' stacks into a single stack, two kinds of operations are allowed:


  • Split: a single stack can be split into two stacks by lifting any top portion of the stack and putting it aside to form a new stack.
  • Join: two stacks can be joined by putting one on top of the other. This is allowed only if the bottom plate of the top stack is no larger than the top plate of the bottom stack, that is, the joined stack has to be properly ordered.


Note that a portion of any stack may never be put directly on top of another stack. It must first be split and then the split portion must be joined with the other stack. Given a collection of stacks, you have to find the minimum number of operations that transforms them into a single stack. The following example corresponds to the sample input, and shows how two stacks can be transformed to a single stack in five operations:

\epsfbox{p6036.eps}

Input

Each test case starts with a line containing a single integer n (1$ \le$n$ \le$50), the number of stacks that have to be combined for a shipment. This is followed by n lines, each describing a stack. These lines start with an integer h (1$ \le$h$ \le$50), the height of the stack. This number is followed by h positive integers that give the diameters of the plates, from top to bottom. All diameters are at most 10 000. These numbers will be in non-decreasing order.

Output

For each test case, display the case number and the minimum number of operations (splits and joins) that have to be performed to combine the given stacks into a single stack.

Sample Input

23 1 2 42 3 534 1 1 1 14 1 1 1 14 1 1 1 1

Sample Output

Case 1: 5Case 2: 2
题解
一道神奇的dp.由于最后从上到下的一堆,所以可以先离散,原数据是10000的嘛。
假设我们进行了x次split操作; 则 我们总共需要的操作次数为x+x+n-1 因为进行一次split操作,连通块的个数就会递增1; 进行x次的话,就有x+n块; 然后把这x+n合并成一块的话; 需要x+n-1次join操作; 再加上x次split操作;总共就是2*x+n-1次操作; 然后我们考虑最后所有的方块都叠成了一块的情形 假设,我们一开始把n堆的每一堆都染成同一种颜色; 则最后只剩一堆方块的时候,肯定是不同颜色相间的;设最后只剩一堆方块的时候,上下相邻的单个方块之间,颜色不同的对数为cnt; 则答案就为2*cnt-n+1 为什么不是上面分析的2*x+n-1次操作? 因为这里的cnt并不是分离的次数; 想一想,如果没有进行分离操作,直接全部叠在一个上面,肯定也有n-1个对是不相等的颜色; 则我们求出的cnt如果想变成分离的次数应该减去2*(n-1) 即2*(cnt-2*(n-1))+n-1 = 2*cnt-n+1 这样,就能根据最后答案的那一叠方块知晓答案了; (也可以参考代码上写的理解方式)这可以给我们的DP提供思路; 我们就对最后的答案的那一堆进行DP; 在DP之前;我们先将所有的盘离散化一下; 因为盘的直径最大达到了10000; 直接枚举盘的直径可能会跨了很多; 而盘子的数目最多为2500 然后,同一堆里的盘子,如果它的直径是一样的,那就把它们都当成一个盘子就好了; 之后; 考虑最后的答案构成的一堆盘子; 肯定是从上到下,盘子的直径不下降的; 则我们,从上到下,依次枚举每个盘子,它要放在哪一堆上面; 设f[i][j]表示,直径小于等于i(已经离散化过)的盘子,把它们都放到第j堆盘子上,最小的相邻不同颜色对数; 对于f[i][]这个状态肯定是移动到已经有i这个盘子的堆上比较划算; 设为j 则转移的时候; 只要找有i-1这个盘子的堆k; 然后用f[i-1][k]这个状态转移到f[i][j] 算一下会增加多少个相邻不同颜色就好; 转移的过程实际上就是其他i盘子移动到那个j上,以及k那一堆i-1盘子之上的整个移动到i上面来 (注意如果k那一堆的i-1盘子下面是i盘子的话,可以省掉一个不同的对数) j和k相不相同要分类讨论,因为是两种情况; 因为允许放在“空地”上,所以j和k相同的情况也是ok的; 最后在f[cnt][1..n]中找最小值; 
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define inf 100000000using namespace std;const int maxn=2501; pair<int,int >a[maxn];int cnt[maxn],f[maxn][maxn],h[maxn][maxn];//f[i][j]表示将前i个数放在第j堆所要切得最小刀数 int c,m,t,n,index1,ans;void prepare(){int p;sort(a+1,a+c+1);c=unique(a+1,a+c+1)-a-1;for(int i=1;i<=c;i++){p=i;++m;while(a[p].first==a[p+1].first&&p<c){p++;}for(int j=i;j<=p;j++){h[m][a[j].second]=1;}cnt[m]=p-i+1;i=p;}}int dp(){memset(f,127/3,sizeof(f));for(int i=1;i<=n;i++){f[1][i]=cnt[1]-h[1][i];}for(int i=2;i<=m;i++){for(int j=1;j<=n;j++){for(int k=1;k<=n;k++){if((j!=k||cnt[i]==1)&&h[i][k]==1)f[i][j]=min(f[i][j],f[i-1][k]+cnt[i]-1);//一个i连着k这堆一起移 else f[i][j]=min(f[i][j],f[i-1][k]+cnt[i]);}if(h[i][j]==0) f[i][j]++;//如果j这堆有i这个盘子,就需要多移一步。 }}ans=inf;//假设每一堆盘子都有一个不可移动的大盘子 for(int i=1;i<=n;i++){ans=min(ans,f[m][i]-n+1);//所以要减去(n-1)刀 }}int main(){while(scanf("%d",&n)==1){memset(cnt,0,sizeof(cnt));memset(h,0,sizeof(h));m=c=0;for(int i=1;i<=n;i++){scanf("%d",&t);for(int j=1;j<=t;j++){scanf("%d",&a[++c].first);a[c].second=i; }}prepare();dp();/*for(int i=1;i<=n;i++)cout<<f[m][i]<<endl;*/printf("Case %d: %d\n",++index1,ans*2+n-1);}return 0;}