日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/10 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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

總結

以上是生活随笔為你收集整理的HDU3183 A Magic Lamp —— 贪心(单调队列优化)/ RMQ / 线段树的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。