hdu 5763 The All-purpose Zero (貪心/數據結構+dp)


The All-purpose Zero

                            Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
                                          Total Submission(s): 935    Accepted Submission(s): 451

Problem Description
?? gets an sequence S with n intergers(0 < n <= 100000,0<= S[i] <= 1000000).?? has a magic so that he can change 0 to any interger(He does not need to change all 0 to the same interger).?? wants you to help him to find out the length of the longest increasing (strictly) subsequence he can get.
 
Input
The first line contains an interger T,denoting the number of the test cases.(T <= 10)
For each case,the first line contains an interger n,which is the length of the array s.
The next line contains n intergers separated by a single space, denote each number in S.
 
Output
For each test case, output one line containing “Case #x: y”(without quotes), where x is the test case number(starting from 1) and y is the length of the longest increasing subsequence he can get.
 
Sample Input
2 7 2 0 2 1 2 0 5 6 1 2 3 3 0 0
 
Sample Output
Case #1: 5 Case #2: 5
Hint
In the first case,you can change the second 0 to 3.So the longest increasing subsequence is 0 1 2 3 5.
 
Author
FZU
 
Source
 
         題意:給了一個序列,其中0可以變成任意一個整數,求最長上升子序列長度。

         思路:貪心+dp:看着題解,到現在還不是理解的很徹底,先引用一下題解,抽時間再理解理解。

       題解:0可以轉化成任意整數,包括負數,顯然求LIS時儘量把0都放進去必定是正確的。因此我們可以把0拿出來,對剩下的做O(nlogn)的LIS,統計結果的時候再算上0的數量。爲了保證嚴格遞增,我們可以將每個權值S[i]減去i前面0的個數,再做LIS,就能保證結果是嚴格遞增的。

       線段樹+dp:還是數據結構好,雖然代碼多,但好理解,而且複雜度和貪心+dp的一樣。

        dp[i]表示長度爲i的上升序列結尾最小是多少。當A[i] == 0 時,其實對於dp數組就是整體加1後右移,也就是for each i ,dp[i+1]=dp[i]+1 ,這裏可以拿線段樹來實現,區間更新(加1),左邊界左移。不過有一點值得注意:dp數組要初始化爲 -INF(或小於-1000000的任一個值)。不然1 0 2 0 1 0 4 0 5 0運行出來可能是6,答案是7,有的程序這個數據不對也能過了,總之數據比較水。具體可以看代碼。


        聽說拿樹狀數組或二分+單調棧也可以實現。不過這題數據比較水,當時隊友拿n^2的直接78ms就過了,他在A[i]==0的時候拿循環更新的。


詳細見代碼:

          貪心+dp:

                

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1e5+100;
#define INF 0x3f3f3f3f
int dp[maxn];
int A[maxn];
int main()
{
    int t,case1=0;
    scanf("%d",&t);
    while(t--)
    {
        int n;
        int i,j;
        printf("Case #%d: ",++case1);
        scanf("%d",&n);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&A[i]);
            dp[i]=INF;
        }
        dp[0]=INF;
        int ans=0;
        for(i=1;i<=n;i++)
        {    
            if(A[i]==0)
                ans++;
            else
                *lower_bound(dp,dp+n,A[i]-ans)=A[i]-ans;
        }
        printf("%d\n",(int)(lower_bound(dp,dp+n+1,INF)-dp+ans));
    }
    return 0;
}
        


          線段樹+dp:

        

//時間202ms
//用線段樹B求最長上升子序列長度
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int maxn=1e5+100;
int A[maxn];
int B[maxn<<2];
int cnt[maxn<<2];
int n;
inline void push_down(int rt)  //區間更新的延遲更新
{
    if(cnt[rt])
    {
        cnt[rt<<1]+=cnt[rt];
        cnt[rt<<1|1]+=cnt[rt];
        B[rt<<1]+=cnt[rt];
        B[rt<<1|1]+=cnt[rt];
        cnt[rt]=0;
    }
}
void update(int L,int R,int l,int r,int rt)//區間更新,整體+1
{
    if(L<=l&&R>=r)
    {
        B[rt]++;
        cnt[rt]++;
        return ;
    }
    int mid=(l+r)>>1;
    push_down(rt);
    if(L<=mid) update(L,R,lson);
    if(R>mid) update(L,R,rson);
    B[rt]=max(B[rt<<1],B[rt<<1|1]);
}
void change(int pos,int val,int l,int r,int rt) //改變pos位置的值
{
    if(l==r)
    {
        B[rt]=val;
        return ;
    }
    int mid=(l+r)>>1;
    push_down(rt);
    if(pos<=mid) change(pos,val,lson);
    else change(pos,val,rson);
    B[rt]=max(B[rt<<1],B[rt<<1|1]);
}
void work(int val,int l,int r,int rt)  //LIS把長度爲i的結尾的值改爲更小的值val,直接利用線段樹的結構來更新
{
    if(l==r)
    {
        B[rt]=val;
        return;
    }
    int mid=(l+r)>>1;
    push_down(rt);
    if(B[rt<<1]>=val) work(val,lson);
    else work(val,rson);
    B[rt]=max(B[rt<<1],B[rt<<1|1]);
}
void solve()    
{
    int ll=n;
    int rr=n;
    int i;
    for(i=1;i<=n;i++)//優化一下dp數組剛開始時的範圍,因爲後面要左邊界左移,優化一下,避免開更大的內存
    {
        if(A[i]!=0)
            rr--;
    }
    ll=rr+1;
    for(i=1;i<=n;i++)
    {
        if(A[i]==0)
        {
            if(rr>=ll)
            {
                update(ll,rr,1,n,1); //整體加1
            }
            ll--;//左邊界左移
        }
        else
        {//這裏就是LIS的內容了,B是線段樹的數組,也就是dp數組吧
            if(B[1]<A[i]) 
            {
                rr++;
                change(rr,A[i],1,n,1);
            }
            else
            {
                work(A[i],1,n,1);
            }
        }
    }
    printf("%d\n",rr-ll+1);
}
int main()
{
    int t,case1=0;
    scanf("%d",&t);
    while(t--)
    {
        printf("Case #%d: ",++case1);
        scanf("%d",&n);
        int i,j;
        for(i=1;i<(maxn<<2);i++)  //線段樹初始爲-INF
        B[i]=-INF;
        memset(cnt,0,sizeof cnt);
        for(i=1;i<=n;i++)
        {
            scanf("%d",&A[i]);
        }
        solve();
    }
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章