码迷,mamicode.com
首页 > 其他好文 > 详细

长郡冬令营集训:荷马史诗 2(epic)

时间:2018-01-03 22:43:47      阅读:247      评论:0      收藏:0      [点我收藏+]

标签:复杂   turn   设计   编码   int   freopen   关心   sort   register   

原题题目:

题目背景

YCB要发送的一篇文章中有 n 种不同的单词,
从 1 到 n 进行编号,其中第 i 种单词出现的总次数为 ci。
YCB的电报机只能发送两种信号,其中 - (长)信号耗时为 2s ; . (短)信号耗时为 1s。
请你为YCB设计一种电报编码,使得他发送整篇文章的总耗时最短。
当然,为了准确无误地接收到信息,任何一个字符的编码串都不能是另一个的前缀。
编码串 Str1 被称为编码串 Str2 的前缀 , 当且仅当:
存在 \(1 ≤ t ≤ m\),使得 Str1 与 Str2[1..t] 相等。
其中, m 是字符串 Str2 的长度, Str2[1..t] 表示 Str2 的前t个字符组成的字符串。

输入输出格式

输入第一行为 n ,第二行 n 个数分别表示 c i 。
输出一行一个数,表示最小总耗时。

样例输入输出

样例输入
4
1 2 2 3
样例输出
22
样例说明
在一种可行的最优方案中,我们给四种字符的编码依次为\(--\) , \(-.\) , \(.-\) , \(..\)
,总耗时=1 × 4 + 2 × 3 + 2 × 3 + 3 × 2 = 22(sec)最优.

样例输入2
15
9 4 3 3 10 10 8 10 3 1 4 1 8 6 4
样例输出2
445

数据范围:

对于 15% 的数据, \(n ≤ 5\)
对于 40% 的数据, \(n ≤ 30\)
对于 55% 的数据, \(n ≤ 100\)
对于 100% 的数据, \(1≤ n ≤ 750, 1≤ ci ≤ 10^5\)

题目解法:

引论:

首先无包含前缀编码肯定是用哈夫曼树。
所以构建一棵哈夫曼树,然后分配编码即可。
有一个显然的贪心,把出现次数排序后,深度小的一定是匹配次数多的。
显然,每向下扩展一层,新增贡献为还未放置的数之和
利用这个我们尝试构造哈夫曼树。

对于 15%(n<=5) 的数据:

用并查集暴力枚举一下树的形态,然后分配深度即可。
枚举树的形态部分的代码:

void dfs(int x,int ret){
    if(x==n){
        ans=min(ret,ans);
        return;
    }
    for(RG int i=1;i<=n;i++)
        for(RG int j=1;j<=n;j++)
            if(fa[i]==i&&fa[j]==j&&i!=j){//把j合并到i中
                fa[j]=i;
                t[i]+=t[j];
                dfs(x+1,ret+t[i]+t[j]);
                //i向上编一层需要t[i]个,j向上编一层需要2*t[j]个
                t[i]-=t[j];
                fa[j]=j;
        }
}
//t为当前节点的size大小。

对于 40%(n<=30) 的数据,

考虑以深度来动态规划:
由于每扩展一个点,会向深度+1与深度+2分别增加一个点。
所以设: \(f[i][j][a][b]\)表示处理到树的第i层,还剩前j小的数没有安置,本层有a个节点,下一层有b个节点。
那么枚举在本层选k个节点放置,剩下的节点扩展。转移为:
\[f[i][j][a][b] = min(f[i+1][j-k][b+(a-k)][k] + ∑c[t],t∈[1,j-k])\]
这样的时间复杂度为\(O(N^5)\),滚掉一维后空间复杂度为\(O(N^3)\)

对于 55%(n≤100)的数据,

观察到上面转移中i就是去打酱油的。
我们不需要关心实际上到了多少层,只需要关心这一层放多少。
所以\(f[i][a][b]\)表示还剩前i小的数没有安置,本层有a个节点,下一层有b个节点。
转移与上面类似:
\[f[i][a][b] = min(f[i-k][b+(a-k)][k] + ∑c[t],t∈[1,j-k])\]
这样的时间复杂度为\(O(N^4)\),空间复杂度为\(O(N^3)\)

对于 100%(n<=750) 的数据:

再观察发现上面枚举的k也是没卵用的。
我们再改变一下状态:
\(f[i][j][k]\)表示还剩前 i 小的数没有安置,本层有 j 个空位,下一层有 k 个空位。
注意:这里定义 空位是指还未确定用途,可以放数也可以扩展的点。
那么转移有两种:
第一种:本层不放,向下扩展一层:
\[f[i][k+j][j] = min(f[i][k+j][j],f[i][j][k] + ∑c[t],t∈[1,j])\]
第二种:在本层放下一个:
\[f[i-1][j-1][k] = min(f[i-1][j-1][k],f[i][j][k])\]
用这两种转移即可。
时间复杂度为\(O(N^3)\),滚掉一维后空间复杂度位\(O(N^2)\),可以跑过所有测试点。

满分AC代码:

#include<bits/stdc++.h>
#define RG register
#define IL inline
#define ll long long
#define gi(x) scanf("%lld",&x)
#define maxn 755
#define INF 1e16+7
using namespace std;

ll f[2][maxn][maxn],c[maxn],n;

int main(){
    freopen("epic.in","r",stdin);
    freopen("epic.out","w",stdout);
    gi(n);
    for(RG ll i = 1; i <= n; i ++)gi(c[i]);
    sort(c+1,c+n+1);
    for(RG ll i = 1; i <= n; i ++)c[i] += c[i-1];
    memset(f,127,sizeof(f));
    f[n&1][1][1] = c[n];
    for(RG ll i = n; i >= 1; i --)
    {
        RG ll p = (i&1) , q = (p^1);
        memset(f[q],127,sizeof(f[q]));
        for(RG ll j = 0; j <= i; j ++)
            for(RG ll k = 0; j+k <= i; k ++)
                f[p][k+j][j] = min(f[p][k+j][j] , f[p][j][k] + c[i]);
        for(RG ll j = 1; j <= i; j ++)
            for(RG ll k = 0; j+k <= i; k ++)
                f[q][j-1][k] = min(f[q][j-1][k] , f[p][j][k]);
    }
    cout<<f[0][0][0];
    return 0;
}

长郡冬令营集训:荷马史诗 2(epic)

标签:复杂   turn   设计   编码   int   freopen   关心   sort   register   

原文地址:https://www.cnblogs.com/GuessYCB/p/8185497.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!