Stacking Plates UVA

来源:互联网 发布:sqlserver 字符串截取 编辑:程序博客网 时间:2024/06/05 20:10

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

其实应该很容易想到,如果我们把不同的堆刷成不同的颜色。那么操作数就与最终状态的相邻异色对的数量ans有关 写几组数据后可以发现最终答案为2*ans-n+1。
那么 我们就可以把问题转化成为求最小异色对数。
如果定义dp[i][j] 为处理完半径小于等于i的圆盘 放在最下面的圆盘为第j种颜色。 这样定义的正确性和转移方程都很容易写。
但是因为i=10000 所以单次询问复杂度为O(10000*(n^2)) 再加上多组输入 数据变态一点(其实好像数据很弱,官方下载的数据全都是单组输入)很有可能超时。
然后因为最多只有2500个盘子 我们其实可以离散化一下。
然后就很(超)好(麻)写(烦)了。
WA了不知道多少发了 结果发现状态转移时没取最小值 ORZ。
代码写得很乱 没有可参考性

# include<iostream># include<cstdio># include<cstring># include<algorithm>#include<vector>#define mem(a,b) memset(a,b,sizeof a)using namespace std;int dp[2505][50];// dp[i][j]表示前i种直径的圆盘处理完后 第i种圆盘以第j堆结尾时的最小异色对数class node{public:    int r,sum,id;    bool  operator <  (const node & b) const    {        return r<b.r;    }};bool judge[55][10005];vector<node> V[2505];node nodes[55][55];int hahs[2555];int blog[10005];int num[55];void init(){    mem(nodes,0),mem(judge,0),mem(dp,0x3f),mem(num,0);    for(int i=0; i<2505; i++)        V[i].clear();}int main(){    int n;    int cases=0;    while(~scanf("%d",&n))    {        int tot=0;        int T;        init();        tot=0;        for(int i=0; i<n; i++)        {            int sum;            scanf("%d",&sum);            int per=-1;            int cnt;            int len=0;            for(int j=0; j<sum; j++)            {                scanf("%d",&cnt);                judge[i][cnt]=1;                if(cnt!=per)                {                    per=cnt;                    hahs[tot++]=cnt;                    nodes[i][len].id=i;                    nodes[i][len].r=cnt;                    nodes[i][len].sum++;                    num[i]++;                    len++;                }                else                {                    nodes[i][len-1].sum++;                }            }        }        sort(hahs,hahs+tot);        tot=unique(hahs,hahs+tot)-hahs;        for(int i=0; i<tot; i++)        {            blog[hahs[i]]=i;        }        for(int i=0; i<n; i++)// n堆        {            for(int j=0; j<num[i]; j++)//每堆num[i]个元素            {                int r=nodes[i][j].r;                V[blog[r]].push_back(nodes[i][j]);            }        }        for(int i=0; i<V[0].size(); i++)        {            dp[0][V[0][i].id]=V[0].size()-1;            //printf("fuck:%d\n",dp[0][i]);        }        for(int i=1; i<tot; i++)        {            for(int j=0; j<V[i].size(); j++)            {                int id1=V[i][j].id;//代表当前圆盘属于第几堆                for(int k=0; k<n; k++)//k代表属于第几堆                {                    if(!judge[k][hahs[i-1]]) continue;                    /// dp[i][j]表示前i种直径的圆盘处理完后 第i种圆盘以第j堆结尾时的最小异色对数                    if(id1!=k)                    {                        int cnt=V[i].size();                        dp[i][id1]=min(dp[i-1][k]+cnt-(judge[k][V[i][j].r]),dp[i][id1]);                    }                    else                    {                        if(V[i].size()==1)                            dp[i][id1]=min(dp[i][id1],dp[i-1][k]);                        else                            dp[i][id1]=min(dp[i][id1],(int)(dp[i-1][k]+V[i].size()));                    }                    //printf("dp[%d][%d]=%d\n",i,id1,dp[i][id1]);                }            }        }        int ans=1<<30;        for(int i=1; i<=n; i++)            ans=min(dp[tot-1][i-1],ans);        printf("Case %d: %d\n",++cases,2*ans-n+1);    }}
原创粉丝点击