zl程序教程

您现在的位置是:首页 >  其它

当前栏目

【Uva 1289】Stacking Plates

UVa
2023-09-14 09:03:49 时间

Link:

Description

有n(1≤n≤50)堆盘子,第i堆盘子有hi个盘子(1≤hi≤50),从上到下直径不减。所有盘 子的直径均不超过10000。有如下两种操作。
split:把一堆盘子从某个位置处分成上下两堆。
join:把一堆盘子a放到另一堆盘子b的顶端,要求是a底部盘子的直径不超过b顶端盘子 的直径。
你的任务是用最少的操作把所有盘子叠成一堆

Solution

假设我们进行了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]中找最小值;

NumberOf WA

5

Reviw

按照递增的顺序枚举i;
看看1..i这一些盘子在哪一堆上面;
在哪一堆的情况都枚举一下;
就能得到所有的情况了;
主要是先想到,每个相邻的颜色不同的对数决定了最后答案;

Code

#include <bits/stdc++.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define LL long long
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define ms(x,y) memset(x,y,sizeof x)
#define ri(x) scanf("%d",&x)
#define rl(x) scanf("%lld",&x)
#define rs(x) scanf("%s",x+1)
#define oi(x) printf("%d",x)
#define ol(x) printf("%lld",x)
#define oc putchar(' ')
#define os(x) printf(x)
#define all(x) x.begin(),x.end()
#define Open() freopen("F:\\rush.txt","r",stdin)
#define Close() ios::sync_with_stdio(0)

typedef pair<int,int> pii;
typedef pair<LL,LL> pll;

const int dx[9] = {0,1,-1,0,0,-1,-1,1,1};
const int dy[9] = {0,0,0,-1,1,-1,1,-1,1};
const double pi = acos(-1.0);
const int N = 2500;
const int M = 50;
const int INF = 0x3f3f3f3f;

int n,have[M+10][N+10],a[M+10][M+10],b[N+10],cnt;
int f[N+10][M+10];
vector <int> v[N+100];

int main(){
    //Open();
    //Close();
    int kk = 0;
    while (~ri(n)){
        ms(f,INF),ms(have,0);;
        cnt = 0;
        rep1(i,1,N) v[i].clear();
        rep1(i,1,n){
            ri(a[i][0]);
            rep1(j,1,a[i][0]) {
                ri(a[i][j]);
                b[++cnt] = a[i][j];
            }
            a[i][0] = unique(a[i]+1,a[i]+1+a[i][0])-a[i]-1;
        }

        sort(b+1,b+1+cnt);
        cnt = unique(b+1,b+1+cnt) - b - 1;

        rep1(i,1,n){
            rep1(j,1,a[i][0]){
                int x = lower_bound(b+1,b+1+cnt,a[i][j]) - b;
                have[i][x] = 1;
                v[x].pb(i);
            }
        }

        int len = v[1].size();
        rep1(i,0,len-1)
            f[1][v[1][i]] = len-1;

        rep1(i,2,cnt){
            int lenj = v[i].size(),lenk = v[i-1].size();
            rep1(jj,0,lenj-1){
                int j = v[i][jj];
                rep1(kk,0,lenk-1){
                    int k = v[i-1][kk];
                    if (j!=k)
                        f[i][j] = min(f[i][j],f[i-1][k] + (int) v[i].size() -
                                        (have[k][i]?1:0));
                    else{
                        int cost;
                        if ( (int) v[i].size() == 1)
                            cost = 0;
                        else
                            cost = (int) v[i].size();
                        f[i][j] = min(f[i][j],f[i-1][k] + cost);
                    }
                }
            }
        }
        int ans = INF;
        rep1(i,1,n)
            ans = min(ans,f[cnt][i]);
        os("Case ");oi(++kk);os(": ");oi(ans*2-n+1);puts("");
    }
    return 0;
}