bzoj4278[ONTAK2015]Tasowanie bzoj1692[USACO 2007Dec]队列变换(Best Cow Line) 贪心正确性证明...
做法網上到處都有就不說了.
這題其實是之前做的….不過由于人太傻現在才想明白比較字典序進行貪心的正確性….
方便起見,在兩個串的最右端都加上很大但不相同的字符,避免第lcp+1個字符不存在的邊界。
如果兩個串當前最左端的字符不相同顯然選較小的.
否則,設兩個剩下的串的lcp長度為x,那么兩個串的第lcp+1個字符(此時必然都存在這個字符,因為我們之前在右端加了很大的哨兵)必然不同.不妨假設第一個串的第lcp+1個字符較小.
?
考慮先選第二個串的第1個字符的某種方案.
如果這種方案中,我們先選了第二個串的第lcp+1個字符再選第一個串的第lcp+1個字符,那么把這種方案在選擇第二個串的第lcp+1個字符之前的所有操作中選第一個串的操作改為選第二個串,選第二個串的操作改為選第一個串,最后把選擇第二個串的第lcp+1個字符改為選擇第一個串的第lcp+1個字符,這樣得到先選第一個串的第1個字符且字典序更小的方案.
如果這種方案中,我們先選了第一個串的第lcp+1個字符,那么第一次操作后第一個串選了0個字符,第二個串選了1個字符.選擇第一個串的第lcp+1個字符后,第一個串選了lcp+1個字符,第二個串選了<=lcp個字符.一開始第二個串選的字符多,最后第一個串選的字符多,因為每次只能進行一個操作,中間必然存在某次操作,使得這次操作后兩個串選擇的字符數目相同.那么我們把這次操作和這次操作之前的操作都反轉一下(即:原先這次操作選第一個串,反轉后這次操作選第二個串,原先選第二個串,反轉后選第一個串),之后的操作不變,就可以得到一個字典序相同但是先選第一個串的第1個字符的方案.
于是,對于任何一個先選字典序較大的串的方案,我們都可以找到一個至少不會更差的方案先選字典序較小的串.因此最優方案必然是每次選擇字典序較小的串.
某奶牛題poj3623&bzoj1692是從一個字符串兩側拿出字符組成字符串要求字典序最小,同樣可以分這樣兩種情況考慮,由選字典序大的一側方案得到選字典序小的一側的方案,且使得最終結果不會更差.兩側開始的串的lcp可能會有重疊部分,拿這個串可能會拿著拿著拿到另一端,但仍然可以進行操作的反轉,因此還是可以這么證.
#include<cstdio> #include<algorithm> using namespace std; const int maxn=400005; int sa[maxn],rank[maxn]; int tmp1[maxn],tmp2[maxn],key[maxn],sum[maxn]; int a[maxn]; void getsa(int n,int m){int *rk=tmp1,*res=tmp2,i,j,p;for(i=0;i<m;++i)sum[i]=0;for(i=0;i<n;++i)sum[rk[i]=a[i]]++;for(i=1;i<m;++i)sum[i]+=sum[i-1];for(i=n-1;i>=0;--i){sa[--sum[rk[i]]]=i;}for(j=1,p=0;p<n;j<<=1,m=p){for(p=0,i=n-j;i<n;++i)res[p++]=i;for(i=0;i<n;++i)if(sa[i]>=j)res[p++]=sa[i]-j;for(i=0;i<n;++i)key[i]=rk[res[i]];for(i=0;i<m;++i)sum[i]=0;for(i=0;i<n;++i)sum[rk[i]]++;for(i=1;i<m;++i)sum[i]+=sum[i-1];for(i=n-1;i>=0;--i)sa[--sum[key[i]]]=res[i];for(res[sa[0]]=0,p=1,i=1;i<n;++i){if(sa[i]+j<n&&sa[i-1]+j<n&&rk[sa[i]]==rk[sa[i-1]]&&rk[sa[i]+j]==rk[sa[i-1]+j])res[sa[i]]=p-1;else res[sa[i]]=p++;}swap(rk,res);}for(int i=0;i<n;++i)rank[sa[i]]=i; } int main(){int n,m;scanf("%d",&n);for(int i=0;i<n;++i)scanf("%d",&a[i]);a[n]=100000;scanf("%d",&m);for(int i=1;i<=m;++i)scanf("%d",&a[n+i]);a[n+m+1]=100000;getsa(n+m+2,100001);int pt1=0,pt2=n+1;int lim=n+m;for(int i=1;i<=lim;++i){printf("%d ",rank[pt1]>rank[pt2]?a[pt2++]:a[pt1++]);}return 0; } #include<cstdio> #include<cctype> #include<algorithm> using namespace std; const int maxn=60005; int tmp[2][maxn],sum[maxn],key[maxn],sa[maxn],rank[maxn]; char str[maxn]; void getsa(int n,int m){int i,j,k,p,*rk=tmp[0],*res=tmp[1];for(i=0;i<m;++i)sum[i]=0;for(i=0;i<n;++i)sum[rk[i]=str[i]]++;for(i=1;i<m;++i)sum[i]+=sum[i-1];for(i=n-1;i>=0;--i)sa[--sum[rk[i]]]=i;for(j=1,p=0;p<n;m=p,j<<=1){for(i=0;i<m;++i)sum[i]=0;for(p=0,i=n-j;i<n;++i)res[p++]=i;for(i=0;i<n;++i)if(sa[i]>=j)res[p++]=sa[i]-j;for(i=0;i<n;++i)sum[key[i]=rk[res[i]]]++;for(i=1;i<m;++i)sum[i]+=sum[i-1];for(i=n-1;i>=0;--i)sa[--sum[key[i]]]=res[i];for(res[sa[0]]=0,p=1,i=1;i<n;++i){res[sa[i]]=(rk[sa[i]]==rk[sa[i-1]]&&rk[sa[i]+j]==rk[sa[i-1]+j])?p-1:p++;}swap(res,rk);}for(i=0;i<n;++i)rank[sa[i]]=i; } int main(){int n;scanf("%d",&n);for(int i=0;i<n;++i){while(str[i]=getchar(),!isgraph(str[i]));}for(int i=0;i<n;++i)str[n+i+1]=str[n-i-1];str[n]='Z'+1;str[2*n+1]='Z'+2;getsa(2*n+2,256);int pt1=0,pt2=n+1;int cnt=0;for(int i=1;i<=n;++i){if(rank[pt1]<rank[pt2]){printf("%c",str[pt1]);pt1++;}else{printf("%c",str[pt2]);pt2++;}cnt++;if(cnt%80==0)printf("\n");}return 0; }?
轉載于:https://www.cnblogs.com/liu-runda/p/6478318.html
總結
以上是生活随笔為你收集整理的bzoj4278[ONTAK2015]Tasowanie bzoj1692[USACO 2007Dec]队列变换(Best Cow Line) 贪心正确性证明...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 了解栈内存堆内存
- 下一篇: 潍职e校帮APP功能简介