HDU3183 A Magic Lamp —— 贪心(单调队列优化)/ RMQ / 线段树

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3183


题解:

方法一:贪心。

在草稿纸上试多几次可以知道,删除数字中从左到右最后一位递增(可以等于)的数字,可以得到最小值,在这个基础下,又继续删除最后一位递增的数字,得到的依然是最小值。这就表明当前这步的贪心不仅是当前最优,而且对于下一步贪心来说也是最优的。所以每次删除最后递增项就可以了。


初期代码(每次循环找最后递增项):

 

Accepted318346MS1408K1259 BG++

 

 

#include<cstdio>//hdu3183 贪心,删除不严格递增序列的最后一个元素
#include<cstring>
#include<algorithm>
#define MAX(a,b) (a>b?a:b)
#define LL long long
#define mod 1000000007using namespace std;int main()
{int n,m;char dig[1005],ans[1005];while(scanf("%s%d",dig,&m)!=EOF){n = strlen(dig);if(n<=m){puts("0");continue;}for(int i = 0; i<m; i++){//每次从头开始找递增序列的最后一个元素int j = 0,last = 0,de = 0;for(j = 1;j<=n-1; j++){if(dig[j]==0) continue;if(dig[last]<=dig[j])//用last记录上次的最后一个递增元素,以便跳过已经被删除的元素last = j;else break;}dig[last] = 0;//将递增序列的最后一个元素标记,删除}int cnt = 0;for(int i = 0; i<n; i++)//将未被删除的导入数组中,if(dig[i]) ans[cnt++] = dig[i];int j = 0;while(j<cnt-1 && ans[j]=='0')//跳过前导0,但要留最后一位,因为答案可能就为0j++;while(j<cnt)putchar(ans[j++]);putchar('\n');}return 0;
}

 



后来发现:每一次都循环找出递增项,其实已经重复操作了。因为在上一次删除中,前面的数字肯定是递增的,这就不用再重新扫一次了,只需要判断当前数字是否也递增,如果递增,则继续下一个数字,如果不是,则将前面的数字删除,直到前面的数字<=当前数字或者删除完毕。这样单调队列就派上用场了。

 

Accepted318315MS1404K1003 BG++

 

代码如下:

 

#include<cstdio>//hdu3183 单调队列
#include<cstring>
#include<algorithm>
#define MAX(a,b) (a>b?a:b)
#define LL long long
#define mod 1000000007using namespace std;char q[1005];int main()
{int n,m;char a[1005];while(~scanf("%s%d",a,&m)){n = strlen(a);if(n<=m){puts("0");continue;}int rear = 0, cnt = 0;int i;for(i = 0; i<n; i++){while(rear>0 && cnt<m && a[i]<q[rear])rear--, cnt++;if(cnt==m) break;q[++rear] = a[i];}while(rear>0 && cnt<m)//没有删除够,继续删rear--, cnt++;while(rear>0)//将队列里的元素倒入数组中,准备输出a[--i] = q[rear--];while(i<=n-2 && a[i]=='0') i++;//跳过前导0;但要留最后一位,因为答案可能就为0for(;i<n; i++)putchar(a[i]);putchar('\n');}return 0;
}


 



方法二:RMQ or 线段树

 

问题可以转化为:在这n个数字中选n-m个数(只能从左往右一次选),使得组成的数最小。

可知第一个数字必定在0~n-1-(m-1),即0~n-m之内取得,且取最小的数字。设第一个数取得的位置为pos,则取得第二个数的范围为:pos+1~n-m+1, 然后又将pos设为取得第二个数的位置,则取得第三个数的范围为:pos+1~n-m+2 …………

查询区间最小值可以用RMQ或者线段树实现。


RMQ:

 

#include<cstdio>//hdu3183 RMQ
#include<cstring>
#include<algorithm>
#include<cmath>
#define MIN(a,b) (a<b?a:b)
#define LL long long
#define mod 1000000007using namespace std;char s[1005], ans[1005];
int n,m,st[1005][20];//st存最值得下标int Get_min(int x, int y)
{return (s[x]<=s[y]?x:y);
}int init_RMQ()
{for(int i = 0; i<n; i++)st[i][0] = i;for(int j = 1; (1<<j)<n; j++)for(int i = 0; i+(1<<j)-1<n; i++)st[i][j] = Get_min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}int find_k(int le, int ri)
{int k = log(ri-le+1)/log(2);return Get_min(st[le][k],st[ri-(1<<k)+1][k]);
}int main()
{while(~scanf("%s%d",s,&m)){n = strlen(s);m = n-m;init_RMQ();int pos = 0,cnt = 0;while(m){pos = find_k(pos,n-m);ans[cnt++] = s[pos++];m--;}int i = 0;for(; i<cnt-1; i++)if(ans[i]!='0') break;if(cnt==0)putchar('0');else for(; i<cnt; i++)putchar(ans[i]);putchar('\n');}return 0;
}


 


线段树:

注意:在建树时,下标为mid的元素要归到左边去

如果归到右边:

设le=3,ri=4;

mid = (le+ri)/2 = 3;

build(le,mid-1); //实际为: build(3,2) 出错

build(mid,ri);//实际为:build(3,4),即又为原始的le和ri, 永久执行下去……

代码如下:

 

#include<cstdio>//hdu3183 线段树
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long longusing namespace std;int n,m;
char s[1005], ans[1005];struct node
{int pos,le,ri;
}tree[4005];void build(int u, int le ,int ri)
{tree[u].le = le;//将结点所指向的范围保存到结点中tree[u].ri = ri;if(le==ri){tree[u].pos = le;return;}int mid = (le+ri)/2;build(u*2,le,mid);//左右建树build(u*2+1,mid+1,ri);if(s[tree[u*2].pos]<=s[tree[u*2+1].pos])tree[u].pos = tree[u*2].pos;elsetree[u].pos = tree[u*2+1].pos;
}int query(int u,int x, int y)
{int le = tree[u].le, ri = tree[u].ri;if(le==x && ri==y)return tree[u].pos;int mid = (le+ri)/2;if(y<=mid) return query(u*2,x,y);//查询范围在左边else if(x>mid) return query(u*2+1,x,y);//查询范围在右边//else return (s[query(u*2,x,mid)]<=s[query(u*2+1,mid+1,y)]?tree[u*2].pos:tree[u*2+1].pos); //有误else//查询范围被分成两段{int xx = query(u*2,x,mid);int yy = query(u*2+1,mid+1,y);if(s[xx]<=s[yy]) return xx;return yy;}
}int main()
{while(~scanf("%s%d",s,&m)){n = strlen(s);m = n-m;build(1,0,n-1);int pos = 0,cnt = 0;while(m){pos = query(1,pos,n-m);ans[cnt++] = s[pos++];m--;}int i = 0;for(; i<cnt-1; i++)if(ans[i]!='0') break;if(cnt==0)putchar('0');else for(; i<cnt; i++)putchar(ans[i]);putchar('\n');}return 0;
}


 

转载于:https://www.cnblogs.com/DOLFAMINGO/p/7538744.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/269695.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python入门知识大全_python基础知识有哪些

Python基础知识&#xff1a;1、引用和对象&#xff1b;2、可变数据类型和不可变数据类型&#xff1b;3、引用传递和值传递&#xff1b;4、深拷贝和浅拷贝&#xff1b;5、基本数据类型&#xff1b;6、关键字、标识符和内置函数&#xff1b;7、算术、赋值运算符等等。Python基础知…

Django开发1

Django开发1 初识Django1.安装django2.创建项目2.1 在终端2.2 Pycharm 3. 创建app4.快速上手4.1 再写一个页面4.2 templates模板4.3 静态文件4.3.1 static目录4.3.2 引用静态文件 5.模板语法//Django开发案例&#xff1a;联通新闻中心6.请求和响应案例&#xff1a;用户登录7.数…

编程式事务和声明式事物

编程式事务 声明式事物

【每日算法】基数排序算法

1&#xff09;算法简介 基数排序是一种非比较型整数排序算法&#xff0c;其原理是将整数按位数切割成不同的数字&#xff0c;然后按每个位数分别比较。由于整数也可以表达字符串&#xff08;比如名字或日期&#xff09;和特定格式的浮点数&#xff0c;所以基数排序也不是只能使…

wincc 报警记录 mysql_如何才能把WINCC报警记录中的数据通过SQL取上来-工业支持中心-西门子中国...

回答者&#xff1a; 凌波微步- 高级工程师&nbsp&nbsp第11级2009-03-09 08:54:18您可以评论本回答&#xff0c;或直接点击“提交”按钮推荐本回答...我可以取出来&#xff0c;但显示不了。我显示时用的是ms listbox 2.0控件&#xff0c;不过没成功显示出来&#xff0c;多…

AES加密算法256位密钥与128位密钥的不同之处

一、指代不同 1、256位密钥&#xff1a;AES的区块长度固定为256位&#xff0c;密钥长度则可以是256。 2、128位密钥&#xff1a;AES的区块长度固定为128位&#xff0c;密钥长度则可以是128。 二、安全性不同 1、256位密钥&#xff1a;256位密钥安全性高于128位密钥。 2、1…

Linux LNMP 环境的搭建之【Nginx的安装】

Nginx 俄罗斯人开发的一款比较强劲的web服务器,静态页面html 单机在优化得当的情况下&#xff0c;能够达到3-4W的并发,是一款比较优秀的服务器软件,在国内,有Tengine【淘宝】开发的分支 ,关于Tengine的文档,可以参考,淘宝团队的官方文档&#xff1a;http://tengine.taobao.org/…

如何把map的value转为list_Java 8 将Map转换为List

将一个Java示例转换Map为List汇总&#xff1a;Map map new HashMap<>();// Convert all Map keys to a ListList result new ArrayList(map.keySet());// Convert all Map values to a ListList result2 new ArrayList(map.values());// Java 8, Convert all Map keys…

非对称加密和对称加密的区别

一 :概述 在现代密码学诞生以前&#xff0c;就已经有很多的加密方法了。例如&#xff0c;最古老的斯巴达加密棒&#xff0c;广泛应用于公元前7世纪的古希腊。16世纪意大利数学家卡尔达诺发明的栅格密码&#xff0c;基于单表代换的凯撒密码、猪圈密码&#xff0c;基于多表代换的…

PING检查网络是否畅通

可用于检测网络畅通情况 using System.Net;using System.Net.NetworkInformation;namespace PING{class Program{static void Main(string[] args){Console.WriteLine("请输入IP");string str Console.ReadLine();Console.WriteLine(ping(str));Console.ReadLine();…

linux安装mysql不成功怎么处理_Linux上安装MySQL时出现不兼容的解决办法

Linux上安装MySQL时出现不兼容的解决办法在linux 上安装mysql rpm的时候,安装不成功MySQL-client-5.5.36-1.linux2.6.x86_64.rpm MySQL-server-5.5.36-1.linux2.6.x86_64.rpm[rootbogon develop]# rpm -ivh MySQL-server-5.5.36-1.linux2.6.x86_64.rpm准备中… ###############…

事务默认的传播属性和事务默认的隔离级别

事务的传播属性 事务的隔离级别&#xff0c;用的就是当前数据的隔离级别

倍增LCA code[vs]1036商务旅行

n个点用n-1条边连接&#xff0c;求两个点间的最短路 显然可以想到用floyd预处理&#xff0c;但复杂度过高 所以一些巨发明了LCA 为什么这类最短路问题要找最近公共祖先&#xff0c;这是一个显然的问题&#xff0c;最近公共祖先说简陋了就是在这个“树”上找一个“转折点" …

java过滤器的原理_Java 三大器之过滤器(Filter)工作原理

Filter 简介filter称为过滤器&#xff0c;可以对web 服务器管理的所有资源: Jsp, Servlet&#xff0c;静态图片文件或静态html文件等进行拦截&#xff0c;从而实现一些特殊的功能。例如实现URL级别的权限访问控制&#xff0c;过滤敏感词汇&#xff0c;压缩响应信息等高级功能。…

Python文件操作-文件的增删改查

需求:对文件进行增删改查 由于时间原因,本次代码没有增加任何注释,如有疑问,请联系编辑者:闫龙 其实我也是醉了,看着这些个代码,我脑袋也特么大了,没办法,大神说了,不让用新知识,只可以使用学过的,所以代码复用就不用说了,根本不可能了,希望大家可以看懂这些,别人啃着糟心,自己…

spring事务三大接口

1.事务三大接口 PlatformTransactionManager 事务管理器TransactionDefinition 事务的一些基础信息&#xff0c;如超时时间、隔离级别、传播属性等TransactionStatus 事务的一些状态信息&#xff0c;如是否一个新的事务、是否已被标记为回滚PlatformTransactionManager //根据…

与你相遇——博客园

显示注册博客园已经一年多了&#xff0c;但是却没有一篇文章&#xff0c;从今天开始每天有学习&#xff0c;每天就必有输出。平常自己的学习效率往往很低&#xff0c;有时经常感动自己学了多少东西&#xff0c;可是记住的不多&#xff0c;想通过记笔记方式掩饰战略上的懒惰&…

java 类隔离_Java类装载体系中的隔离性

正文Java中类的查找与装载出现的问题总是会时不时出现在Java程序员面前&#xff0c;这并不是什么丢脸的事情&#xff0c;相信没有一个Java程序员没遇到过ClassNotException,因此不要为被人瞅见自己也犯这样的错误而觉得不自然&#xff0c;但是在如果出现了ClassNotFoundExcepti…

java用easyexcel实现读取excell表格内容

引入依赖 <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.6</version></dependency><!-- https:…