[HYBZ 1563/NOI2009 day1 诗人小G]决策单调优化、动态规划**

题目地址:http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1563

【算法分析】

容易得到方程f[i]=min(f[j]+|sum[j]-sum[i]+j-i-1-L|^P)

令w(j,i)=|sum[j]-sum[i]+j-i-1-L|^P

则f[i]=min(f[j]+w[j,i])

这是经典的1D/1D模式的DP方程。

首先我们来证明一个性质:

命题1:对于i

证明:

令g(x)=|x|^P

1、P为偶数,对其求导得g(x)’=P*x^(P-1),是单调递增的。

2、P为奇数,对其求导得g(x)’=P*x^(P-1)*(x/|x|),容易发现,同样是单调递增的。

由于导函数单调递增,而导函数的定积分又对应于g(x)的变化量。

若a

而对于我们的w函数,随着i的增加,w[j,i]的增量是相同的,

所以相当于把g(x)不断向左平移

由于f[i]又是固定的常数,所以命题1得证。

下面再证明另外一个性质:

命题2:方程满足决策单调性。

方程满足凸四边形不等式<=>方程满足决策单调性

w[i,j]满足凸四边形不等式当且仅当:

固定j以后,将w[i,j+1]-w[i,j]表示为i的函数,该函数的导数恒<=0。

(这个结论位于《算法艺术与信息学竞赛》的152页,定理3下面)

令g(i)=w[i,j+1]-w[i,j],即g(i)=sum[j+1]-sum[i]+1-(sum[j]-sum[i])=a[j+1]+1。

所以g(i)为常数函数,g'(i)=0。

满足条件。

所以,综上所述,命题2得证。

于是我们就可以开一个双端队列维护决策序列了。

这个序列每一个域包含3个元素,两个是表示该点造成最优决策的区间,第3个是表示这个点是哪个。

然后我们维护这个队列即可得到最优解。

维护方法:

头部:顺次下去,如果i超过区间尾就到下一个决策区间。

尾部:每次把新弄好的i试图插进去,从后面开始,如果它负责的区域都没有i好,那么就把它删掉。由于命题1,我们只需要判断开头的结点就可以了。

当然,如果它负责的区域包含的<=i的点,那么就不可能删掉它了,因为i这个点不能完全替代它。

所以最后不可能把队列的元素都删掉,然后对队尾元素进行二分,看什么时候i这个点能够超越它。(这依赖于命题1的性质)

然后修改区间就可以了。

【其它】1A

【CODE】

#include #include #include #include using namespace std;
const int N=101111;
const long long INF=(long long)(1000000000)*(long long)(1000000000);
char S[N][33];
struct Queue_Type{int l,r,p;}Q[N];
int n,P,L,head,tail;
int a[N],sum[N],fa[N],v[N];
long long f[N];

double w(int j,int i){
    int x=sum[i]-sum[j]-1-L;
    if (x<0) x=-x;
    double ans=1;
    for (int i=1;i<=P;i++) ans*=x;
    ans+=f[j];
    return ans;
}   

long long lw(int j,int i){
    int x=sum[i]-sum[j]-1-L;
    if (x<0) x=-x;
    long long ans=1;
    for (int i=1;i<=P;i++) ans*=x;
    ans+=f[j];
    return ans;
}

void input(){
    cin >> n >> L >> P;
    sum[0]=0;
    for (int i=1;i<=n;i++){
        scanf("%s",S[i]+1);
        a[i]=strlen(S[i]+1);
        sum[i]=sum[i-1]+a[i]+1;
    }   
    sum[n+1]=sum[n]+1;
}   

void insert(int i){
    if (i==n) return;
    while (Q[tail].l>i && w(Q[tail].p,Q[tail].l)>=w(i,Q[tail].l)){
        tail–;
        Q[tail].r=n;
    }   
    int l=i+1,r=n,mid;
    if (Q[tail].l>i+1) l=Q[tail].l;
    while (l        mid=(l+r)/2;
        if (w(Q[tail].p,mid)>=w(i,mid)) r=mid;
                                   else l=mid+1;
    }
    if (w(Q[tail].p,l)    Q[++tail].p=i;
    Q[tail].l=l;
    Q[tail].r=n;
    Q[tail-1].r=l-1;
}   

void work(){
    memset(Q,0,sizeof(Q));
    head=0; tail=0; Q[0].l=1; Q[0].r=n; Q[0].p=0; f[0]=0;
    for (int i=1;i<=n;i++){
        while (i>Q[head].r) head++;
        if (w(Q[head].p,i)>INF+10 || lw(Q[head].p,i)>INF) f[i]=-1;
        else{   
          f[i]=lw(Q[head].p,i);
          fa[i]=Q[head].p;
          insert(i);
        }   
    }   
}   

void output(){
    if (f[n]==-1){
        printf("Too hard to arrangen");
        return;
    }   
    cout << f[n] << endl;
/*    memset(v,0,sizeof(v));
    for (int i=n;i;i=fa[i]) v[i]=1;
    for (int i=1;i<=n;i++){
        printf("%s",S[i]+1);
        if (v[i]) printf("n");
             else printf(" ");
    }    */
}   

int main(){
    freopen("poet.in","r",stdin);
    freopen("poet.out","w",stdout);
    int tc;
    scanf("%d",&tc);
    for (int i=1;i<=tc;i++){
        input();
        work();
        output();
        printf("——————–n");
    }   
}

[POJ 3415 Common Substrings]后缀数组、分组思想、单调栈

【题目大意】

给定两个串,让你求满足A[i..i+k]=B[j..j+k]且k>=K的(i,j)的数目。

【算法分析】

先把两个串连在一起,中间加一个特殊字符。

然后建立后缀数组和height数组。

最后对height进行线性分组,同组的height都>=K

然后注意到我们需要统计的是每一个点对(i,j)其中i为A串的后缀,j为B串的后缀,他们的lcp(i,j)-limit+1,然后加起来。但是我们注意到越向后延伸,对于同一个点的lcp只会单调递减,然后用一个栈合并lcp已经被并为同一个数字的区间,最后输出就可以。

【其它】3WA,s1和s2忘记搞成long long了。。。

表示紧紧跟随lcc的脚步。。。

【CODE】

#include

[HYBZ 1010]斜率优化的动态规划

【题目】

http://61.187.179.132:8080/JudgeOnline/showproblem?problem_id=1010

【算法分析】

好不容易终于看懂了,我还是链接一下算了。。。

http://www.byvoid.com/blog/noi-2009-poet/

模仿他的解法三即可。。。

【CODE】

#include #include #include #include #define sqr(x) ((x)*(x))
using namespace std;
const int N=60000;
int n,L,head,tail,a[N],qp[N];
long long qf[N],f[N],sum[N];

void init(){
     scanf("%d%d",&n,&L);
     for (int i=1;i<=n;i++){
         scanf("%d",&a[i]);
         a[i]++;
     }
     sum[0]=0;
     for (int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
}

double xl(int i,int j){
       return (f[j]+sqr(sum[j])-f[i]-sqr(sum[i]))/(sum[j]-sum[i]);
}

void work(){
     f[0]=0;
     int head=1, tail=1;
     qf[1]=0; qp[1]=0;
     for (int i=1;i<=n;i++){
         while (head         int &j=qp[head];
         f[i]=f[j]+sqr(sum[i]-sum[j]-1-L);
         while (head         tail++;
         qp[tail]=i;
         qf[tail]=f[i];
     }
     cout << f[n] << endl;
}

int main(){
    init();
    work();
}

[POJ 2752]KMP**

【题目大意】

求所有对于同一字符串的前缀和后缀相等的位置。

【算法分析】

KMP,然后一直从n一直next回去,路上的都是。

为什么呢?

首先从在n的第一次next是找最长的S[1,K]=S[n-K+1,n]

这个容易理解。

然后接下来的next是求最长的S[1,K]=S[J-K+1,J]

注意了,这个S[J-K+1,J]必然与S[n-K+1,n]相等。(可以用数学归纳法证明一下)

于是对应下一个前缀等于后缀的位置。

所以,这样一直下去就会找到所有的前缀等于后缀的位置。

【其它】1A

http://hi.baidu.com/aekdycoin/blog/item/fbe5a03233bd1648ad4b5f1c.html

紧紧跟随师傅的脚步。

【CODE】

#include #include #include

int next[500000],n,ans[500000],ansl;
char s[500000];

int main(){
    int i,j;
    while (scanf("%sn",&s[1])!=EOF){
          n=strlen(s+1);
          next[1]=0;
          j=0;
          for (i=2;i<=n;i++){
              while (j && s[j+1]!=s[i]) j=next[j];
              if (s[j+1]==s[i]) j++;
              next[i]=j;
          }
          ansl=1; ans[1]=n;
          j=n;
          while (1){
                if (next[j]==0) break;
                j=next[j];
                ans[++ansl]=j;
          }
          for (i=ansl;i>=1;i–){
              printf("%d",ans[i]);
              if (i!=1) printf(" ");
          }
          printf("n");
    }
}

HDU月赛——2010.3.6

这次月赛终于不是一个人单挑了。

CWJ&&FHN&&CZM组成了一队。

然后说说比赛经历:

一开始12点开始,FHN和CZM都没在。。。于是我开了第一题。

写了一会儿,好像很繁琐,然后FHN和CZM陆续回来了。

然后FHN一下就把最后一题秒了,YM。。。

然后我碌碌无为,第一题没搞定,跳过。

看第2题,一看出题人,AekdyCoin,是师傅那题防AK的题目,于是再次跳过。

第三题,看着是个最大独立集问题。但是好像是任意图的。百度一下,发现MS是NP问题?跳过。。

然后CZM大概浏览了题目,告诉我第四题是字符串,让我去做。于是我一看,KMP,1A。

然后CZM和FHN看后面的题目,发现都挺神,于是他们把目光聚焦在那题比较多人AC的题目上,然后CZM一个犀利的背包把它搞定了。

然后FHN出去买电脑

剩下我和CZM,然后我表示第一题突然有了想法,线段树,因为long long WA了两次,终于AC。期间肥闽各种看题目,不过MS没什么结果。

然后我回看第三题,推了一些性质,然后交了个贪心,WA。。。CZM犀利地给了我一个反例。。。

最后我推出一个重要性质:

∵出题人戴牛

∴必然可以网络流

于是尝试了各种二分图构图,终于WA了2次以后AC。

然后FHN回来了,和CZM一起在那里猜数字。。。

我去搞1009,发现应该是在AC自动机上的DP。但是空间好像卡着卡着。。。

纠结到剩下半小时,放弃,和他们一起猜数字。。。

然后得到的结论:

1、没有billion,million….and so on…

2、字符串长度14

然后我说:直接枚举吧,大不了(26+10+1)*14。。。

然后CZM就先去搞那个字符集,为我们枚举作为前提,后来很可惜,没有搞完,结束了。

一共就出了5题。。。。

其实和第一名的差距就是1009——AC自动机上的DP 以及 1005——猜数字。

猜数字其实想通了不难,但是一开始没有意识到是单case。。。

RANK:18

围观地址:http://acm.hdu.edu.cn/vip/contest_ranklist.php?cid=237&page=1

这次的猜数字,绝对NB,赞一个,不过在OI的话没法出。