P4683 [IOI2008] Type Printer 打印机
题意描述
几百年前的 IOI 的题目还是很好的呀。
给你一个 诡异的 打印机,它只能用已有的字符来打印,而且必须每一个都用到。(这岂不是活字印刷术)
你可以对其执行三个操作:
- 打印,用大写字母 P 表示,输出顺序任意,但仅能且必须用到当前打印机里的每一个字符。
- 插入,输入一个字符 c,表示在打印机中插入字符 c。(打印机的存储是一个队列)
- 删除,从队列尾部删除一个字符。
给定 \(N\) 个字符串,问当前需要至少多少步才能完成所有打印。
算法分析
考虑可爱的 \(trie\) 树,先建一棵树,假设当前已经建好了。(如果不会 你做这道题干什么 可以看看这篇文章)
然后我们发现题目变成了:确定一个 \(DFS\) 的顺序,使得树上的每一个点都遍历到,并且结束于某个叶节点。
思想一
显然,倘若要求回到根节点,步数永远为 \(2\times (N-1)\)。(每条边都经过两次)
但是最后一条路径可以不回去,假设最后打印的字符串长度为 \(len\),显然最终遍历步数为 \(2\times (N-1)-len\)。
注意:这里的遍历步数 \(\neq\) 答案步数,因为答案中还有删除操作。
显然 \(ans_{min}=2\times (N-1)-len_{max}\)。
思想二
既然每一个单词都要输出,打印的操作次数一定 = 总字符串数。
那么关键就是插入与删除次数尽量少,那么显然倘若要求删除次数尽量少,之前插入的字符长度应当尽量小。
所以最长的单词当然要最后打印。
当然以上的证明是不严谨的,但是有助于大家理解。
所以通过两种方法都可以确定贪心策略:最后走最长的单词,其他随意。
代码实现
其实还挺简单的。
- 建树。不用讲解吧。
- 标记。标记一下最长的字符串,也很简单。
- dfs。最重要的环节了。在 dfs 时主要有三种操作:打印,向下遍历,回溯。
在打印时通过判断当前是否为单词的尾部来进行判断。
向下遍历时特殊处理被标记的节点。
回溯时倘若该点不是被标记的点,就打印 "-"。
看不懂的话看代码吧:
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
#include<string>
#define N 500010//几次空间开小了都 RE,所以索性开大一点,dalao 勿喷。
using namespace std;
int n,trie[N][30],tot=1;
bool sum[N],flag[N],finish=false;
//上面三个分别用来标记:是否是单词结尾(trie 基本操作),是否是最长字符串上的点,是否到了最后一个单词。
string a,jl,ans;
void insert(string a){
int p=1;
for(int i=0;i<a.length();i++){
int ch=a[i]-'a';
if(!trie[p][ch]) trie[p][ch]=++tot;
p=trie[p][ch];
}
sum[p]=true;
return;
}
//常规插入操作。
void Mark(string a){
int p=1;
for(int i=0;i<a.length();i++){
int ch=a[i]-'a';
p=trie[p][ch];
flag[p]=true;
}
return;
}
//标记操作,顺着 trie 走一遍就好了。
void dfs(int now){
if(sum[now]){
ans+='P';//打印情况。
//注意这里一定不要写 return;
//因为有可能这是一个单词的前缀,这样就少了一个甚至更多的单词。
}
int x=-1;
for(int i=0;i<26;i++){
int t=trie[now][i];
if(!t) continue;
if(flag[t]) x=i;//记录最长串上的点,最后遍历。
else{
ans+=(i+'a');
dfs(t);
}
}
if(x!=-1){
ans+=(x+'a');
dfs(trie[now][x]);
}//最后遍历的最长串。
if(flag[now] && x==-1)//当遍历到了最长串的最后一个点,就不用再回退(删除)了。
finish=true;
if(!finish) ans+='-';//回溯时删除。
return;
}
int main(){
scanf("%d",&n);
memset(flag,false,sizeof(flag));
memset(sum,false,sizeof(sum));
memset(trie,0,sizeof(trie));//不必要的初始化。
for(int i=1;i<=n;i++){
cin >> a;insert(a);
if(jl.length()<a.length()) jl=a;
}//寻找最长串。
Mark(jl);
dfs(1);
printf("%d\n",ans.length());
for(int i=0;i<ans.length();i++)
printf("%c\n",ans[i]);
return 0;
}
结语
无耻安利 blog。
简单的 \(trie + dfs\),感觉挺简单的...。
主要是要有题目简化以及将题目转化为抽象数据结构的能力。
完结撒花。
P4683 [IOI2008] Type Printer 打印机的更多相关文章
- bzoj AC倒序
Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...
- Python调用打印机参考例子
参考资料: http://blog.csdn.net/jdh99/article/details/42585987 http://www.oschina.net/question/1438043_23 ...
- Spring学习笔记:Spring动态组装打印机
一.如何开发一个打印机 1.可灵活配置使用彩色魔盒或灰色魔盒 2.可灵活配置打印页面的大小 二.打印机功能的实现依赖于魔盒和纸张 三.步骤: 1.定义墨盒和纸张的接口标准 package cn.pri ...
- C# 调用打印机打印文件
C# 调用打印机打印文件,通常情况下,例如Word.Excel.PDF等可以使用一些对应的组件进行打印,另一个通用的方式是直接启用一个打印的进程进行打印.示例代码如下: using System.Di ...
- LDAP注入与防御解析
[目录] 0x1 LDAP介绍 0x2 LDAP注入攻击及防御 0x3 参考资料 0x1 LDAP介绍 1 LDAP出现的背景 LDAP(Lightweight Directory Access Pr ...
- 0031 Java学习笔记-梁勇著《Java语言程序设计-基础篇 第十版》英语单词
第01章 计算机.程序和Java概述 CPU(Central Processing Unit) * 中央处理器 Control Unit * 控制单元 arithmetic/logic unit /ə ...
- Windows WMIC命令使用详解
本文转载出处http://www.jb51.net/article/49987.htm www.makaidong.com/博客园文/32743.shtml wmic alias list brief ...
- Java基础常见英语词汇
Java基础常见英语词汇(共70个) ['ɔbdʒekt] ['ɔ:rientid]导向的 ['prəʊɡræmɪŋ]编程 OO: object ...
- IT软件开发常用英语词汇
Aabstract 抽象的abstract base class (ABC)抽象基类abstract class 抽象类abstraction 抽象.抽象物.抽象性access 存取.访问access ...
随机推荐
- 故意使用free掉的内存的一个实验( 常量区/栈)
故意使用free掉的内存的一个实验 考虑一下两种声明 struct stuff{ char home[10]; int num; char name[10]; }; struct stuff{ cha ...
- Java知识系统回顾整理01基础06数组07数组工具类Arrays
一.Arrays简介 Arrays是针对数组的工具类,可以进行 排序,查找,复制填充等功能. 大大提高了开发人员的工作效率. 二.Arrays提供的数组复制方法 与使用System.arraycopy ...
- 【奇淫巧技】sqlmap绕过过滤的tamper脚本分类汇总
sqlmap绕过过滤的tamper脚本分类汇总
- Linux桌面环境配置
目录 更换软件源 中文输入法 firefox安装flash插件 编译安装Vim 关闭蓝牙开机自启 yakuake无法正常使用 在中文环境下将默认目录修改成英文 电脑换成了thinkpad x1c 20 ...
- 多测师讲解python练习题_100以内奇数,偶数的和_高级讲师肖sir
(1)通过while 循环来求出1-100之和'''(2)通过while 循环来求出1-100奇数之和'''(3)通过while 循环来求出1-100偶数之和''' 奇数和 sum1=0for i i ...
- 关于.netMVC 出现@ViewBag 出现错误(波浪红线)的解决方法
解决vs2015.vs2013解决mvc5 viewbag问题 1.关闭vs2015或者vs2013 打开我的电脑或者文件夹 2.打开我的电脑 在地址栏输入 %UserProfile%\AppData ...
- day45 Pyhton 数据库Mysql 02
一.前期回顾 数据库 mysql的安装 配置环境 为什么要用数据库? 稳定性 一致性 并发 存取数据效率高 数据库的分类 关系型数据库 mysql oracle sqlserver 非关系型数据库 r ...
- 正式班D9
2020.10.16星期五 正式班D9 一.vmware workstation的使用 虚拟机管理软件 定义 虚拟机(Virtual Machine)软件是一套特殊的软件,它可以作为操作系统独立运行, ...
- bash 括号使用
Bash 括号多种使用方式 ${} 变量初始化 ${param:-string} 若变量param为空或者未定义,则用在命令行中用string来替换${param:-string} 否则变量param ...
- Python之集合详解
定义: 1.不同元素组成 2.无序 3.集合中的元素必须是不可变类型 创建集合 s = {1,2,3,4,5,6,7,8} 1.定义可变集合 >>> set_test = set(' ...