题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3183
题解:
方法一:贪心。
在草稿纸上试多几次可以知道,删除数字中从左到右最后一位递增(可以等于)的数字,可以得到最小值,在这个基础下,又继续删除最后一位递增的数字,得到的依然是最小值。这就表明当前这步的贪心不仅是当前最优,而且对于下一步贪心来说也是最优的。所以每次删除最后递增项就可以了。
初期代码(每次循环找最后递增项):
Accepted | 3183 | 46MS | 1408K | 1259 B | G++ |
#include//hdu3183 贪心,删除不严格递增序列的最后一个元素
#include
#include
#define MAX(a,b) (a>b?a:b)
#define LL long long
#define mod 1000000007
using 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
Accepted | 3183 | 15MS | 1404K | 1003 B | G++ |
代码如下:
#include//hdu3183 单调队列
#include
#include
#define MAX(a,b) (a>b?a:b)
#define LL long long
#define mod 1000000007
using 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; i0 && cnt0 && cnt0)//将队列里的元素倒入数组中,准备输出
a[--i] = q[rear--];
while(i<=n-2 && a[i]=='0') i++;//跳过前导0;但要留最后一位,因为答案可能就为0
for(;i
问题可以转化为:在这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//hdu3183 RMQ
#include
#include
#include
#define MIN(a,b) (a
线段树:
注意:在建树时,下标为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//hdu3183 线段树
#include
#include
#include
#define LL long long
using 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;
else
tree[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