1. 程式人生 > >CodeVS 2245 淺談二維線段樹優化間距限制型LCS動態規劃狀態轉移

CodeVS 2245 淺談二維線段樹優化間距限制型LCS動態規劃狀態轉移

這裡寫圖片描述
世界真的很大
終於算是學會了這個什麼二維線段樹了233
今天第二題考了這道題,講道理當時DP方程已經到必須一個二維資料結構來維護的地步了
當時整個人是矇蔽的,這玩意兒我不會啊
然後感覺大家都做出來了,就很慌,懷疑有沒有什麼別的解法,可能是自己想複雜之類的
就想了很久,然後第三題的暴力轉正解就沒有去檢查:
BZOJ 2744
就炸
說一句事後諸葛亮的話,這道題要是我當時會二維線段樹豈不是今天AK?
233那當然是不可能的

看題先:

description:

彭先生任職於證券公司,是一位股票分析師。公司經理認為目前的股票分析軟體仍可再改進,希望彭先生再設計一套更準確的軟體。近日來,彭先生埋頭鑽研,他發現過去的研究結果,有人提到,如果能在歷史資料中,找到與近期股票走勢相近的樣型,即可使用此歷史樣型的交易策略,做為近期的買賣策略。為了驗證這樣的講法是否正確,彭先生從股票歷史資料抽出一些特徵資料,並以大寫英文字母A~Z 代表特徵資料,因此股票資料變成一串的英文字母序列。判斷近期股票資料與某一段歷史資料是否相近,就變成判斷二串字母序列( 長度不一定相等) 的相似度,亦即找出兩者的最長共同子序列(LCS,longest common subsequence) 。

在計算二串股票資料序列的相似度時,還有一個限制,兩個相似點( 相同字母) 的前後間距不能太遠,否則相似度會被扭曲。發現了這個特性後,彭先生將此問題正式定義為「有間距限制的最長共同子序列」(GLCS, gapped longest common subsequence)問題。

假設第一個序列稱為α,第二個序列稱為β。例如,α =“ACBDCAA”,β=“ADDBCDBAC” 。兩者在無間距限制的情形下,其 LCS 可為“ADCA” , “ABCA” ,或“ACBC” ,長度為4。假設間距限制如下:

A 2, B 0, C 3, D 0

上述間距之意義為,如果字母A 被選入LCS 中,則與其前一個被選入的字母之間,在α序列最多隻能有2 個未被選入的字母,在β序列亦同。α與β在上述間距限制的情形,GLCS 可為“ACA” 或“ACC” ,長度為3。

對於無間距限制的情形,可將每個字母的間距視為無限大。本題的答案只要輸出GLCS 的長度即可。

input:

共分成二部分。第一部分,第一行為α序列,第二行為β序列,兩者都是大寫英文字母A~Z 的序列,每個序列長度至少為 1,最長為 800。第二部分自第三行起,第三行有一個數值 k,代表以下有 k 組測試資料(1 ≤ k≤ 5) ,每一組測試資料為一行,每一行有多個( 可能零個) 字母間距限制,每個限制的第一個為英文字母,第二個為間距數值( 數值介於0 與400 之間);英文字母不一定按照順序,也不一定每個字母都會出現,未出現的字母間距可視為無限大。每一行字母間距限制的最後一個符號為$ ,代表該行( 該組) 的資料結束。每一行的字母間距限制情形,相鄰兩項資料之間均有一個空白隔開。

output:

對於每一組測試資料,輸出它的 GLCS 長度。輸出這 k 個值於一行,且相鄰兩個整數之間以一個空白隔開。

首先考慮樸素LCS,直接暴力的話本來就是n^4的,但是為什麼我們可以有n^2的做法呢?
大概就是因為我們不關心匹配是從哪裡來的,i,j這個位置不管能不能匹配都可以取到之前的最大的答案。
所以在遞推時我們就可以把答案“傳遞”或者說“延展”下去
但這樣的結果就是,我們無法得知每一次匹配的上一次匹配位置,從而無法完成這道題的約束條件
所以說優化版本的LCS求法在這道題面前就作廢了,還得回到原先的n^4版本
f(i,j)表示到A的第i位,B的第j位,且i,j可以匹配的最大答案,不能就是0
每次往前面找限制範圍內的最大答案
設i,j可以匹配,匹配字母的間距限制為len
那麼相當於就是在f(i-len,j-len)到f(i,j)的矩陣裡面求一個最值
那麼這一步就可以用二維線段樹來維護,求出來之後在加入ij的答案
需要注意的就是邊界問題的討論與判斷了

現在來談談二維線段樹
本質上是維護一個矩陣的答案
實現方式是通過對橫座標開一個線段樹,此線段樹的每一個節點儲存一顆維護縱座標的線段樹
查詢的時候現在橫座標上找到對應的區間,訪問這個區間的縱座標線段樹然後查詢縱座標對應的區間
修改的時候因為是單點修改,找到橫縱座標的線段樹位置就好
值得注意的是縱座標的線段樹維護的是值,是可以合併的,但是橫座標的線段樹維護的是“縱座標的線段樹”,是不能輕易合併的
所以我們採取的方法就是,並不是先修改再合併,而是在訪問修改節點的路上就把沿途經過的橫座標區間對應的縱座標線段樹修改好,避免合併的問題

完整程式碼:

#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;

const int INF=0x3f3f3f3f;

struct node
{
    int sum;
    node *ls,*rs;
    void update()
    {
        sum=max(ls->sum,rs->sum);
    }
}pool1[8000010],*tail=pool1;

struct Node
{
    node *root;
    Node *ls,*rs;
}pool[8000010],*head=pool,*root;

int n,m,T,ans=0;
int f[3010][3010],src[110];
char A[100010],B[100010],tmp[10];

void init()
{
    ans=0;
    tail=pool1,head=pool;
    memset(f,0,sizeof(f));
    memset(src,INF,sizeof(src));
}

node *built(int lf,int rg)
{
    node *nd=++tail;
    if(lf==rg)
    {
        nd->sum=0;
        nd->ls=nd->rs=0;
        return nd;
    }
    int mid=(lf+rg)>>1;
    nd->ls=built(lf,mid);
    nd->rs=built(mid+1,rg);
    nd->update();
    return nd;
}

Node *build(int lf,int rg)
{
    Node *nd=++head;
    nd->root=built(1,m);
    if(lf==rg)
    {
        nd->ls=nd->rs=0;
        return nd;
    }
    int mid=(lf+rg)>>1;
    nd->ls=build(lf,mid);
    nd->rs=build(mid+1,rg);
    return nd;
}

void modify(node *nd,int lf,int rg,int pos,int delta)
{
    if(lf==rg)
    {
        nd->sum=max(nd->sum,delta);
        return ;
    }
    int mid=(lf+rg)>>1;
    if(pos<=mid) modify(nd->ls,lf,mid,pos,delta);
    else modify(nd->rs,mid+1,rg,pos,delta);
    nd->update();
}

void modify(Node *nd,int lf,int rg,int pos1,int pos2,int delta)
{
    modify(nd->root,1,m,pos2,delta);
    if(lf==rg) return ;
    int mid=(lf+rg)>>1;
    if(pos1<=mid) modify(nd->ls,lf,mid,pos1,pos2,delta);
    else modify(nd->rs,mid+1,rg,pos1,pos2,delta);
}

int query(node *nd,int lf,int rg,int L,int R)
{
    if(L>R) return 0;
    if(L<=lf && rg<=R)
         return nd->sum;
    int mid=(lf+rg)>>1,rt=0;
    if(L<=mid) rt=max(rt,query(nd->ls,lf,mid,L,R));
    if(R>mid) rt=max(rt,query(nd->rs,mid+1,rg,L,R));
    return rt;
}

int query(Node *nd,int lf,int rg,int L1,int R1,int L2,int R2)
{
    if(L1>R1) return 0;
    if(L1<=lf && rg<=R1)
        return query(nd->root,1,m,L2,R2);
    int mid=(lf+rg)>>1,rt=0;
    if(L1<=mid) rt=max(rt,query(nd->ls,lf,mid,L1,R1,L2,R2));
    if(R1>mid) rt=max(rt,query(nd->rs,mid+1,rg,L1,R1,L2,R2));
    return rt;
}

int main()
{
    scanf("%s",A+1);
    scanf("%s",B+1);
    n=strlen(A+1),m=strlen(B+1);
    scanf("%d",&T);
    while(T--)
    {
        init();
        root=build(1,n);
        while(scanf("%s",tmp) && tmp[0]!='$')
        {
            int x;
            scanf("%d",&x);
            src[tmp[0]-'A']=x;
        }
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                if(A[i]==B[j])
                {
                    int R1=i-1,L1=max(1,i-src[A[i]-'A']-1);
                    int R2=j-1,L2=max(1,j-src[A[i]-'A']-1);
                    f[i][j]=query(root,1,n,L1,R1,L2,R2)+1;
                    modify(root,1,n,i,j,f[i][j]);
                }else f[i][j]=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                ans=max(ans,f[i][j]);
        printf("%d ",ans);
    }
    return 0;
}
/*
Whoso pulleth out this sword from this stone and anvil is duly born King of all England
*/

嗯,就是這樣

相關推薦

CodeVS 2245 線段優化間距限制LCS動態規劃狀態轉移

世界真的很大 終於算是學會了這個什麼二維線段樹了233 今天第二題考了這道題,講道理當時DP方程已經到必須一個二維資料結構來維護的地步了 當時整個人是矇蔽的,這玩意兒我不會啊 然後感覺大家都做出來了,就很慌,懷疑有沒有什麼別的解法,可能是自己想複雜之

【BZOJ4785】[Zjoi2017]狀數組 線段

這也 現在 ont 平面 nbsp -s mil 比賽 turn 【BZOJ4785】[Zjoi2017]樹狀數組 Description 漆黑的晚上,九條可憐躺在床上輾轉反側。難以入眠的她想起了若幹年前她的一次悲慘的OI 比賽經歷。那是一道基礎的樹狀數組題。給出

POJ 2019 Cornfields 線段的初始化與最值查詢

popu def comm init 都沒有 data- ont emp class 模板到不行。。連更新都沒有。。。存個模板。 理解留到小結的時候再寫。 #include <algorithm> #include <iostream>

Codeforces 242E. XOR on Segment (線段 lazy操作 xor)

又是 維護 problem -- spa c++ push_back str 題目 題目鏈接: http://codeforces.com/problemset/problem/242/E 題意: 給出一個序列,有兩種操作,一種是計算l到r的和,另一種是讓l到r的數全部和x

UVa 11297 Census (線段)

cpp [0 ons 上一個 highlight ctype comm bitset space 題意:給定上一個二維矩陣,有兩種操作 第一種是修改 c x y val 把(x, y) 改成 val 第二種是查詢 q x1 y1 x2 y2 查詢這個矩形內的最大值和最小值。

BZOJ.4553.[HEOI2016&TJOI2016]序列(DP 狀數組套線段/線段(MLE) 動態開點)4

ini esp mes http mar cnblogs static gpo max 題目鏈接:BZOJ 洛谷 \(O(n^2)\)DP很好寫,對於當前的i從之前滿足條件的j中選一個最大值,\(dp[i]=d[j]+1\) for(int j=1; j<i; ++

BZOJ 4785 [Zjoi2017]狀數組 | 線段

emp http void cnblogs 可能 www 為什麽 esp ++ 題目鏈接 BZOJ 4785 題解 這道題真是令人頭禿 = = 可以看出題面中的九條可憐把求前綴和寫成了求後綴和,然後他求的區間和卻仍然是sum[r] ^ sum[l - 1],實際上求的是閉區

淺顯易懂的標記永久化講解 && POI 2006 Tet-Tetris 3D | 線段

記錄 變量 digi 查詢 今天 class .org ring post 題目:Luogu 3437 這是今天 SLYZ 考試的一道題,一道二維線段樹的入門題,慘的是我之前沒有寫過二維線段樹,更不知道什麽是標記用久化,於是自己 YY 出了標記永久化,但由於我十分的菜所以

+【UVALive】6709 Mosaic 線段

printf target lan uva tdi set type typedef 判斷 題目鏈接:6709 Mosaic 題解:參考這個博客:二維線段樹,先按行建樹然後每一個節點也是一個棵線段樹按列建。 #include<bits/stdc++.h>

BZOJ4785 [Zjoi2017]狀數組 【線段 + 標記永久化】

結合 題解 php 數組 air line pri www ostream 題目鏈接 BZOJ4785 題解 肝了一個下午QAQ沒寫過二維線段樹還是很難受 首先題目中的樹狀數組實際維護的是後綴和,這一點憑分析或經驗或手模觀察可以得出 在\(\mod 2\)意義下,我們實際求

BZOJ1513 [POI2006]Tet-Tetris 3D 【線段

get mem HR void 修改 mod 3D 理解 fine 題目鏈接 BZOJ1513 題解 真正地理解了一波線段樹標記永久化的姿勢 每個節點維護兩個值\(v\)和\(tag\) \(v\)代表兒子中的最值 \(tag\)代表未下傳的最值 顯然節點的區間大於等於\(

BZOJ4785 ZJOI2017狀數組(概率+線段

cal cor += clas str col else 個數 clu   可以發現這個寫掛的樹狀數組求的是後綴和。find(r)-find(l-1)在模2意義下實際上查詢的是l-1~r-1的和,而本來要查詢的是l~r的和。也就是說,若結果正確,則a[l-1]=a[r](m

CodeForces 846D. Monitor(線段/RMQ)

題目大意:給予n,m,k,t 和 t 行i,j,val代表第i行j列的元素會在val天后損壞。求在整張(n*m)圖中求一個k*k的區域的數全部損壞的最小天數。試了一下二維線段樹,總結一下就是和一維的差別不大,從二叉樹變成了四叉樹(三維線段樹豈不是要八叉樹,喪心病狂)。總體上就是把一個長方形均分四塊,然後總的節

【每日一題+線段】HDU - 4819 Mosaic

【每日一題+二維線段樹】HDU - 4819 Mosaic 【題意】 給出一個nn的矩陣,查詢以(x,y)為中心,長度為ll的矩陣中的最大值和最小值,將中心元素替換為floor( (max+min)/2 ) 【思路】 二維線段樹模板 # include<cstdio&g

Check Corners 【HDU - 2888】【線段

題目連結   很多人寫這道題都用的是二維RMQ,但是,我覺得這道題可以鍛鍊一下我二維線段樹的思維,但是,無獨有偶,這道題會卡一些二維線段樹的模板,一開始我想也沒想,直接敲了剛學的線段樹,然後不停的RE,後來改了下,換成單點更新與區間更新二維線段樹,還是不行TLE了,於是,就開始想,

線段【模板——給出對應註釋】

閒話少說,直接看註釋反而會更容易讀懂這段二維線段樹的模板: #include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #include<map>

線段的講解【建立線上段樹上的提升】

二維線段樹       二維線段樹最主要用於平面統計問題。類似一維線段樹,最經典的就是求區間最值(或區間和),推廣到二維,求得就是矩形區域最值(或矩形區域和),對於矩形區域和,二維樹狀陣列更加高效,而矩形區域最值,更加高效的方法是二維RMQ,但是二維RMQ不支援動態

Counting Black 【POJ - 1656】【線段+記憶體優化

題目連結   這道題卡了記憶體,但是處理這個記憶體的方式卻也簡單,可以直接用short int來減少記憶體的使用,於是就可以用四叉樹——二維線段樹過了。 #include <iostream> #include <cstdio> #include &

Mosaic 【HDU - 4819】【線段

題目連結   這道題難就只是難在題目難讀,題意讀懂後就是一道普通的二維線段樹更新查詢問題。   題意:給你一個N*N的矩陣,並且已經建立了初始值,然後給你個點以及L,很多人不解其義,其實就是給你個點,然後查的是以(x, y)為基礎的點,在以左上角(x-L/2, y-L

Matrix Searching 【ZOJ - 2859】【線段

題目連結   簡單的二維線段樹求區間最小值問題,還不用修改操作。 #include <iostream> #include <cstdio> #include <cmath> #include <string> #includ