20141005 【 DP 】 UVa 1289 Stacking Plates

来源:互联网 发布:广州网络推广哪家好 编辑:程序博客网 时间:2024/05/16 14:17

E. Stacking Plates

Time Limit: 3000ms
Memory Limit: 131072KB
64-bit integer IO format: %lld      Java class name: Main
Submit Status PID: 25334

[PDF Link]

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










看了3份,都看不懂。

中国人的,讲的太简单了。。。

/**    思考后可以发现答案就是    分裂次数+合并次数-n-1 = 分裂次数*2-n-1。    接下来考虑如何让分裂次数最小。    将盘子排序,大小相同的盘子视作同一种,设 f[i][j] 表示到第i种盘子,这个盘子来自j堆的最小分裂次数,        枚举上一个盘子属于的堆k,    则如果k可以放在第i-1种盘子的末尾和第i种盘子的开头,    则       f[i][j]=min(f[i-1][k]+拥有第i种盘子的堆数-1),    否则     f[i][j]=min(f[i-1][k]+拥有第i种盘子的堆数)。    而k需要满足的条件,    就是第k堆既拥有第i种盘子,    又拥有第i-1种盘子,    且除非第i种盘子来源仅有j,否则j!=k。    答案即为min(f[m][i])(1<=i<=n)。*/#include<cstdio>#include<cstring>#include<algorithm>using namespace std;pair<int,int> a[2505];int f[2505][55];int h[2505][55];int cnt[2505];int n,c,m;void pre(){    int i,j,p;    sort(a+1,a+c+1);    c=unique(a+1,a+c+1)-a-1;    for (i=1;i<=c;++i)    {    p=i,++m;        while (a[p].first==a[p+1].first&&p<c)            ++p;        for (j=i;j<=p;++j)            h[m][a[j].second]=1;        cnt[m]=p-i+1;        i=p;    }}int dp(){    int i,j,k,ans=0x7ffffff;    memset(f,127,sizeof(f));    for (i=1;i<=n;++i)        if (h[1][i])            f[1][i]=cnt[1];    for (i=2;i<=m;++i)        for (j=1;j<=n;++j)            if (h[i][j])                for (k=1;k<=n;++k)                {    if (h[i][k]&&(cnt[i]==1||k!=j))                        f[i][j]=min(f[i][j],f[i-1][k]+cnt[i]-1);                    else f[i][j]=min(f[i][j],f[i-1][k]+cnt[i]);                }    for (i=1;i<=n;++i)        ans=min(ans,f[m][i]);    return ans*2-n-1;}int main(){    int i,j,t,test=0;    while (scanf("%d",&n)!=EOF)    {    memset(cnt,0,sizeof(cnt));        memset(h,0,sizeof(h));        m=c=0;        for (i=1;i<=n;++i)        {    scanf("%d",&t);            for (j=1;j<=t;++j)            {    scanf("%d",&a[++c].first);                a[c].second=i;            }        }        pre();        printf("Case %d: %d\n",++test,dp());    }    return 0;}



西班牙人的,居然上JAVA,好高端啊,可惜还是看不懂。。

import java.awt.List;import java.util.ArrayList;import java.util.Collections;import java.util.Scanner;public class Main{   public static void main(String[] a3d)   {      Scanner in = new Scanner(System.in);      while(in.hasNextLine())      {         //llena los datos del caso (lista de pilas)         ArrayList<ArrayList<Integer>> lista = new ArrayList<ArrayList<Integer>>();         ArrayList<Integer> ordenada = new ArrayList<Integer>();         int contador = 0;         int cantpilas = in.nextInt();         in.nextLine();         for(int i=0;i<cantpilas;i++){  //llena cada pila            ArrayList<Integer> pila= new ArrayList<Integer>();            String linea=in.nextLine();            String partes[] = linea.split(" ");            for(int j=0;j<partes.length;j++){ //agrega cada plato               if(j!=0){                  pila.add( Integer.parseInt(partes[j]));               }            }            lista.add(pila);         }         //recorre la estructura         for(int i=0;i<lista.size();i++){            for(int j=0;j<lista.get(i).size();j++){               ordenada.add(lista.get(i).get(j));               //System.out.print(lista.get(i).get(j)+" ");            }            //System.out.println("");         }         //System.out.println("");         Collections.sort(ordenada);         for(int i=0;i<ordenada.size();i++){            //System.out.print(ordenada.get(i)+" ");         }         //System.out.println("");         //System.out.println("");         for(int i=0;i<cantpilas;i++){            int a=0;            int b=0;            while(lista.get(i).get(a) > ordenada.get(b)){               b++;            }            while(a<lista.get(i).size() && b<ordenada.size() ){               if(lista.get(i).get(a)==ordenada.get(b)){  //caso son iguales                  //while(lista.get(i).get(a)==ordenada.get(b)){                     b++;               //   }                  a++;                  //System.out.println("caso igual.  a:"+a+" b:"+b);               }else{               if(lista.get(i).get(a) < ordenada.get(b)){ //caso pilas menor                  //System.out.println("caso a menor no se cambia nada.  a:"+a+" b:"+b);                  break;               }else{               if(lista.get(i).get(a) > ordenada.get(b)){ //caso pilas mayor                  while(lista.get(i).get(a)>ordenada.get(b)){                     b++;                  }                  a++;                  contador++;                  //System.out.println("caso a mayor.  a:"+a+" b:"+b);               }               }               }            }         }         //System.out.println("contador:"+(contador*2+cantpilas-1));         System.out.println(contador*2+cantpilas-1);      }   }}



日本人的。。。。

#include <iostream>#include <vector>#include <algorithm>#include <cstring>using namespace std;#define REP(i,x)for(int i=0;i<(int)x;i++)typedef vector<int> Array;vector<Array> plates;vector<int> cntPlates; // ある高さにあるプレートの数vector<Array> hasH; // あるプレートがある高さを含んでいるかvector<Array> hasI; // ある高さにあるプレートの種類int H; // 高さ// 入力 計算量はO(N^3)bool input(){int n;if(!(cin>>n))return false;plates=vector<Array>(n);vector<int> hs;REP(i,n){int k;cin>>k;int t;REP(j,k){cin>>t;if(plates[i].empty()==false&&plates[i].back()==t)continue;plates[i].push_back(t);hs.push_back(t);}}sort(hs.begin(),hs.end());hs.erase(unique(hs.begin(),hs.end()),hs.end());REP(i,n){REP(j,plates[i].size()){plates[i][j]=lower_bound(hs.begin(), hs.end(), plates[i][j])-hs.begin();}}H=hs.size();hasH = vector<Array>(n);REP(i,n){hasH[i] = Array(H+1);REP(j,plates[i].size()){hasH[i][plates[i][j]]=1;}}cntPlates=vector<int>(H+1);hasI=vector<Array>(H+1);REP(i,H+1){REP(j,n){if(hasH[j][i]){cntPlates[i]++;hasI[i].push_back(j);}}}return true;}int dp[52][2502];// 一回毎の計算量はO(N)// 各種類の個数をxiとする// 内部ループがxiで、// DPテーブルの利用量はO(sum(xi)*N) = O(N^2) 均等にある場合xi=Nでこれが最悪ケースなのでは?// 全体の計算量はO(N^3)int dfs(int back,int h){if(h==H)return 0;// back=plates.size();if(dp[back][h]>=0)return dp[back][h];// 同じ高さが何枚あるかint cntSplit=0, cntStack=0, ret=99999999;REP(k,hasI[h].size()){int i=hasI[h][k];if(i==back)continue;if(hasH[i][h]){cntStack++;// 分割しないといけない場合最後はその数以外if(plates[i].back() == h)continue;cntSplit++;}}// 次にどれを選ぶかREP(k,hasI[h].size()){int i=hasI[h][k];if(i==back)continue;if(hasH[i][h]){// 出来るだけ進めるint nh=h+1;while(cntPlates[nh]==1&&hasH[i][nh])nh++;int d=0; // 選択した数の分割が不要かどうかif(plates[i].back() == nh-1 && nh-1 != h) d = -1;if(plates[i].back() == nh && cntPlates[nh]>1) d = -1;ret=min(ret, dfs(i,nh) + cntSplit + cntStack + d);}}return dp[back][h]=ret;}void solve(){memset(dp,-1,sizeof(dp));cout<< dfs(plates.size(),0) - 1 <<endl;}int main() {for(int testCase=1;input();testCase++){cout<<"Case "<<testCase<<": ";solve();}return 0;}


0 0
原创粉丝点击