zoukankan      html  css  js  c++  java
  • 《算法竞赛进阶指南》0x5B四边形不等式 NOI2009诗人小G

    题目链接:https://www.acwing.com/problem/content/description/306/

    给出n行字符串,现在将其排版,定义一个不协调度,dp中只需要记录阶段为前i个句子已经排好版,不需要记录排了多少行。通过dp进行转移之后发现如果用朴素算法一定会超时。

    经过对代价函数的计算发现其满足四边形不等式,所以可以用决策单调性以及队列进行优化。

    其中队列中维护的是一个决策以及决策当前覆盖的区间,也就是在这个当前的区间中,该决策是区间中每个位置的最优决策。

    代码:

    #include<iostream>
    #include<cstdio> 
    #include<algorithm>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    const int N = 1000010;
    int n,l,p;
    ld f[N];
    int s[N];
    struct node{
        int x,l,r;//x为决策,[l,r]表示决策x在当前是f[l~r]的最优决策 
    }q[N];
    ld calc(int i,int j){//采用决策j计算f[i],注意可能会爆double 
        ld ans=1,num=abs((ld)(s[i]-s[j]+i-j-1-l));
        for(int i=1;i<=p;i++)ans*=num;
        return ans+f[j];
    }
    int get(int i,int L,int R){//找到i所属的一段对应的决策 
        int ans;
        while(L <= R){
            int mid=L+R>>1;
            if(q[mid].l<=i && q[mid].r>=i){
                ans=mid;
                break;
            }
            else if(q[mid].r<i)L=mid+1;
            else R=mid-1;
        }
        return q[ans].x;
    }
    void insert(int i,int &L,int &R){
        int w=-1;
        while(L <= R){
            //i是比这一段中原来的决策更优的决策 
            if(calc(q[R].l,i)<=calc(q[R].l,q[R].x))w=q[R--].l;
            else{
                //对r来说,i是更优的决策,但是对l来说,i不是更优的决策 
                if(calc(q[R].r,q[R].x)>calc(q[R].r,i)){
                    int l=q[R].l,r=q[R].r;
                    while(l < r){
                        int mid=l+r>>1;
                        //i更优 
                        if(calc(mid,i) > calc(mid,q[R].x))l=mid+1;
                        else r=mid;
                    }
                    q[R].r=l-1;//l是满足i是最优决策的第一个位置 
                    w=l;
                }
                break;
            } 
        }
        if(w!=-1){
            q[++R].l=w;
            q[R].r=n;
            q[R].x=i;
        }
    } 
    void G(){
        cin>>n>>l>>p;
        for(int i=1;i<=n;i++){
            char str[40];
            scanf("%s",str);
            s[i]=s[i-1]+strlen(str);//前i个字符的长度 
        }
        
        int L=1,R=1;
        q[1].x=0; 
        q[1].l=1;
        q[1].r=n;
        for(int i=1;i<=n;i++){
            int j=get(i,L,R);//查找覆盖i的区间对应的决策 
            f[i]=calc(i,j);//使用决策j计算f[i] 
            while(L<=R && q[L].r<=i)L++;//删除队头的无用决策 
            q[L].l=i+1;
            insert(i,L,R); //使用i决策更新对[i+1,n]的决策 
        }
        if(f[n] > 1e18)puts("Too hard to arrange") ;
        else cout<<(ll)f[n]<<endl;
        puts("--------------------");
    }
    int main(){
        int t;
        cin>>t;
        while(t--){
            G();
        }
        return 0;
    }

     实际上可以再做一次优化,因为队首的区间就是i+1开始的,所以下一个决策直接取队头的就可了,不用get函数取得到包含i+1的区间对应的决策。

    代码:

    #include<iostream>
    #include<cstdio> 
    #include<algorithm>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    typedef long double ld;
    const int N = 1000010;
    int n,l,p;
    ld f[N];
    int s[N];
    struct node{
        int x,l,r;//x为决策,[l,r]表示决策x在当前是f[l~r]的最优决策 
    }q[N];
    ld calc(int i,int j){//采用决策j计算f[i],注意可能会爆double 
        ld ans=1,num=abs((ld)(s[i]-s[j]+i-j-1-l));
        for(int i=1;i<=p;i++)ans*=num;
        return ans+f[j];
    }
    void insert(int i,int &L,int &R){
        int w=-1;
        while(L <= R){
            //i是比这一段中原来的决策更优的决策 
            if(calc(q[R].l,i)<=calc(q[R].l,q[R].x))w=q[R--].l;
            else{
                //对r来说,i是更优的决策,但是对l来说,i不是更优的决策 
                if(calc(q[R].r,q[R].x)>calc(q[R].r,i)){
                    int l=q[R].l,r=q[R].r;
                    while(l < r){
                        int mid=l+r>>1;
                        //i更优 
                        if(calc(mid,i) > calc(mid,q[R].x))l=mid+1;
                        else r=mid;
                    }
                    q[R].r=l-1;//l是满足i是最优决策的第一个位置 
                    w=l;
                }
                break;
            } 
        }
        if(w!=-1){
            q[++R].l=w;
            q[R].r=n;
            q[R].x=i;
        }
    } 
    void G(){
        cin>>n>>l>>p;
        for(int i=1;i<=n;i++){
            char str[40];
            scanf("%s",str);
            s[i]=s[i-1]+strlen(str);//前i个字符的长度 
        }
        
        int L=1,R=1;
        q[1].x=0; 
        q[1].l=1;
        q[1].r=n;
        for(int i=1;i<=n;i++){
            f[i]=calc(i,q[L].x);//使用决策j计算f[i] 
            while(L<=R && q[L].r<=i)L++;//删除队头的无用决策 
            q[L].l=i+1;
            insert(i,L,R); //使用i决策更新对[i+1,n]的决策 
        }
        if(f[n] > 1e18)puts("Too hard to arrange") ;
        else cout<<(ll)f[n]<<endl;
        puts("--------------------");
    }
    int main(){
        int t;
        cin>>t;
        while(t--){
            G();
        }
        return 0;
    }
  • 相关阅读:
    Mac普通用户修改了/etc/sudoers文件的解决办法
    python对缓存(memcached,redis)的操作
    线程、进程、协程和队列
    python作用域和多继承
    sokect编程进阶
    socket编程基础
    python面相对象进阶
    python异常处理
    configparser模块
    subprocess模块
  • 原文地址:https://www.cnblogs.com/randy-lo/p/13438439.html
Copyright © 2011-2022 走看看