zoukankan      html  css  js  c++  java
  • NOI2009 诗人小G

    题目链接:戳我

    30pts做法:
    我们设(dp[i])表示前i个句子最小的代价,然后再记录一个当前的最小值是从哪里转移过来的。
    注意输出的行末不带空格,以及要用long double(double和long long都会炸)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<vector>
    #define MAXN 100010
    #define mp make_pair
    #define ll long long
    #define double long double
    #define INF 1e18
    using namespace std;
    int T,n,l,p;
    char s[MAXN][60];
    double sum[MAXN];
    inline double calc(double x)
    {
        double cur_ans=1,xx=1.0*x;
        for(int i=1;i<=p;i++) cur_ans*=xx;
        return cur_ans;
    }
    namespace subtask1
    {
        int last[MAXN];
        double dp[MAXN];
        vector<pair<int,int> >vec;
        inline void solve()
        {
            vec.clear();
            for(int i=1;i<=n;i++) sum[i]+=sum[i-1];
            for(int i=0;i<=n;i++) dp[i]=INF*8;
            dp[0]=0;
            for(int i=1;i<=n;i++)
            {
                for(int j=0;j<i;j++)
                {
                    double cur_ans=dp[j]+calc(fabs(1.0*l-(1.0*i-j-1+sum[i]-sum[j])));
                    if(cur_ans<dp[i]) 
                    {
                        last[i]=j+1;
                        dp[i]=cur_ans;
                    }
                }
            }
            if(dp[n]>INF)
            {
                printf("Too hard to arrange
    ");
                return;
            }
            printf("%.0Lf
    ",dp[n]);
            int now=n;
            while(now>=1)
            {
                vec.push_back(mp(last[now],now));
                now=last[now]-1;
            }
            for(int i=vec.size()-1;i>=0;i--)
            {
                for(int j=vec[i].first;j<vec[i].second;j++)
                    printf("%s ",s[j]+1);
                printf("%s",s[vec[i].second]+1);
                puts("");
            }
        }
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        freopen("ce.out","w",stdout);
        #endif
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d%d",&n,&l,&p);
            for(int i=1;i<=n;i++) scanf("%s",s[i]+1),sum[i]=1.0*strlen(s[i]+1);
            subtask1::solve();
            printf("--------------------
    ");
        }
        return 0;
    }
    

    100分做法需要决策单调性
    我们考虑记录s数组表示前缀和,(f[i])表示前i个的最小代价。
    (f[i]=min{f[j]+|s[i]-s[j]-1-L|})
    其中(jin [0,i-1])
    显然对于两个点j,k,且(j<k)。如果j能够转移到i,k能转移到i+1,那么j一定不能转移到其它大于i的点。
    那么这就存在一个决策单调的性质。
    我们可以维护一下么个点的作用范围。用一个单调栈加三元组记录就可以了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #include<queue>
    #define MAXN 100010
    #define double long double
    using namespace std;
    int n,L,P,T,head,tail,top;
    int g[MAXN],S[MAXN];
    char ch[MAXN][35];
    long long s[MAXN];
    double f[MAXN];
    struct Node{int j,l,r;}q[MAXN];
    inline double fpow(double x,int y)
    {
        double cur_ans=1;
        while(y)
        {
            if(y&1) cur_ans=cur_ans*x;
            x=x*x;
            y>>=1;
        }
        return cur_ans;
    }
    inline double calc(int i,int j)
    {
        return f[j]+fpow(fabs(1.0*s[i]-s[j]-1-L),P);
    }
    int main()
    {
        #ifndef ONLINE_JUDGE
        freopen("ce.in","r",stdin);
        freopen("ce.out","w",stdout);
        #endif
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d%d",&n,&L,&P);
            for(int i=1;i<=n;i++) 
            {
                scanf("%s",ch[i]+1);
                s[i]=1ll*strlen(ch[i]+1);
            }
            for(int i=1;i<=n;i++) s[i]+=s[i-1]+1;
            memset(q,0,sizeof(q));
            q[head=tail=1]=(Node){0,1,n};
            for(int i=1;i<=n;i++)
            {
                while(head<tail&&q[head].r<i) ++head;
                int j=q[head].j;
                q[head].l++;
                f[i]=calc(i,j);
                g[i]=j;
                while(head<tail&&calc(q[tail].l,q[tail].j)>=calc(q[tail].l,i)) tail--;
                int l=q[tail].l,r=q[tail].r,cur_ans=q[tail].r+1;
                while(l<=r)
                {
                    int mid=(l+r)>>1;
                    if(calc(mid,i)<=calc(mid,q[tail].j)) cur_ans=mid,r=mid-1;
                    else l=mid+1;
                }
                if(cur_ans!=q[tail].l) q[tail].r=cur_ans-1;
                else --tail;
                if(cur_ans<=n) q[++tail]=(Node){i,cur_ans,n};
            }
            if(f[n]>1e18) printf("Too hard to arrange
    ");
            else
            {
                printf("%.0Lf
    ",f[n]);
                top=0;
                for(int i=n;i;i=g[i])S[++top]=i;S[top+1]=0;
                for(int i=top;i;--i)
                    for(int j=S[i+1]+1;j<=S[i];++j)
                    {
                        printf("%s",ch[j]+1);
                        if(j!=S[i]) printf(" ");
                        else printf("
    ");
                    }
            }
            printf("--------------------
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    计算机中丢失MSVCP110.dll
    应用程序无法正常启动0xc000007b
    查看80端口被占用
    重装系统后搭建php环境
    修改、设置root密码
    安装虚拟机时出现The Microsoft Runtime DLL
    linux网络配置
    Java拾遗
    哈希中的那些序问题
    笔记本电脑安装jupyterthemes
  • 原文地址:https://www.cnblogs.com/fengxunling/p/11138474.html
Copyright © 2011-2022 走看看