zoukankan      html  css  js  c++  java
  • 【题解】[TJOI2009] 火星人的手机

    题目信息

    题目来源:CCF 天津省选 2009;

    在线评测地址:Luogu#3860

    运行限制:时间不超过 (1.00 extrm{s}),空间不超过 (128 extrm{MiB})

    题目背景

    你应火星人之邀为他们设计一款新型的手机。我们知道在标准的地球人手机上,数字键共有 (10) 个,(26) 个字母 az 分别与某个数字键相关联,并且一个数字键上的若干字母必须是字母表中连续的一段。比如下图是地球手机的一个标准方案:

    本图由洛谷图床搬运

    题目描述

    我们要输入一个字母,必须连续按它所在的数字键若干次,次数即为这个字母在这个键的第几个位置。例如在上图的方案中,若我们要输入 C,就需要按三次数字键 2;若要输入 M,需按一次数字键 6

    火星人手机的构造与地球人手机类似,上面有 (M) 个火星数字键,你需要把火星文的 (N) 个字母放置在这 (M) 个键上。(同样要求一个数字键上必须是连续的若干个火星字母)

    现在给定一段火星文中各个字母的出现次数,你设计的手机必须使得输入这段文字所需的按键次数最少。

    输入格式

    输入文件的第一行包括两个数字 (N)(M),分别表示火星文字母数和火星手机的按键数。接下来有 (N) 行,每行包含一个数字,依次表示每个字母在文章中的出现次数。这个次数不超过 (1000)

    输出格式

    输出文件的第一行包括一个数字,表示最少的按键次数。

    接下来的 (M) 行表示一种设计方案:每行包含一个数,依次表示每个数字键上有几个火星字母。(这些数字可以为 (0)

    如果有多种方案可以得到最少的按键次数,你需要输出第一个数字键上包含字母最少的方案;如果仍有多种方案,你需要在其中选择第二个数字键上字母最少的方案;依此类推。

    数据规模及约定

    对于 (100\%) 的数据,(1le Nle 500)(1le Mle 100)

    分析

    题意是说给定 (N) 个数,将其分成连续(M) 组,使 (sumlimits_{i=1}^M v_i(i-l_i+1)) 最小,其中 (v_i) 是第 (i) 点的权值,而 (l_i) 是第 (i) 个数所在区间的左端点。并给出一组字典序最小的答案。

    既然都分组了,还是连续的,显然是 DP。令 (f_{i,j}) 为将前 (i) 个数分成 (j) 组的答案。则 (f_{i,j}=min_k{f_{k,j-1} + g(k+1,i)}),其中 (g(x,y)=sumlimits_{i=x}^y v_i(i-x+1))

    注意到 (g(x,y)=sumlimits_{i=x}^y v_i(i-x+1)=sumlimits_{i=x}^{y} iv_i-(x-1)sumlimits_{i=x}^y v_i),可以前缀和优化。

    这样,枚举所有 (f_{i,j}),然后枚举 (k) 转移,复杂度就是 (mathcal{O}(N^2M)),可以 AC。

    注意事项

    初始时,(f_{0,j}=0),其余应该全部赋成 (infty)

    Code

    #include <cstdio>
    #include <cstring>
    using namespace std;
    
    typedef long long ll;
    const int max_n = 100, max_m = 500;
    
    int cnt[max_m], com[max_n][max_m], out[max_n];
    ll dp[max_n+1][max_m+1], pf1[max_m+1], pf2[max_m+1]; // DP 数组和前缀和
    
    ll getval(int l, int r) { return (pf1[r+1] - pf1[l]) - (pf2[r+1] - pf2[l]) * l; } // 相当于 g 函数
    
    int main()
    {
    	memset(dp, 0x3f, sizeof(dp)); // 初始化 1
    
    	int n, m;
    
    	scanf("%d%d", &m, &n);
    	for (int i = 0; i < m; i++)
    		scanf("%d", cnt + i); // 输入
    	
    	pf1[0] = pf2[0] = 0;
    	for (int i = 0; i < m; i++) // 统计前缀和
    	{
    		pf1[i+1] = pf1[i] + cnt[i] * (i + 1);
    		pf2[i+1] = pf2[i] + cnt[i];
    	}
    
    	for (int i = 0; i <= n; i++) // 初始化 2
    		dp[i][0] = 0;
    	
    	for (int i = 1; i <= n; i++)
    		for (int j = 1; j <= m; j++) // dp[i][j]
    			for (int k = 0; k < j; k++) // 枚举转移点
    				if (dp[i][j] > dp[i-1][k] + getval(k, j-1))
    					dp[i][j] = dp[i-1][k] + getval(k, j-1), com[i-1][j-1] = k; // 取最大值
    	
    	printf("%lld", dp[n][m]); // 先输出答案
    	for (int i = n - 1, j = com[n-1][m-1], k = m; i >= 0; i--, k = j, j = com[i][k-1]) // 反向统计转移点
    		out[i] = k - j;
    	
    	for (int i = 0; i < n; i++) // 倒序后输出即可
    		printf("
    %d", out[i]);
    
    	return 0; // 然后就 AC 了、
    }
    
  • 相关阅读:
    使用SpringMVC的crud操作时,进行数据修改,但是修改成功后,页面无法显示lastName属性值(被修改的那条记录)
    使用SpringMVC时报错HTTP Status 405
    使用springmvc时报错org.springframework.beans.NullValueInNestedPathException: Invalid property 'department' of bean class [com.atguigu.springmvc.crud.entities.Employee]:
    快速傅里叶变换应用之二 hdu 4609 3-idiots
    离散傅立叶变换与快速傅立叶变换(DFT与FFT)
    深度优先搜索(DFS)递归形式改为非递归形式
    模拟题组
    STL--自定义类型的排序
    多关键字的快速排序
    二叉树的前序和中序得到后序 hdu1710
  • 原文地址:https://www.cnblogs.com/5ab-juruo/p/solution-tjoi2009_lg3860.html
Copyright © 2011-2022 走看看