zoukankan      html  css  js  c++  java
  • 【线性DP】【lgP1336】最佳课题选择

    传送门

    Description

      Matrix67要在下个月交给老师n篇论文,论文的内容可以从m个课题中选择。由于课题数有限,Matrix67不得不重复选择一些课题。完成不同课题的论文所花的时间不同。具体地说,对于某个课题i,若Matrix67计划一共写x篇论文,则完成该课题的论文总共需要花费Ai*x^Bi个单位时间(系数Ai和指数Bi均为正整数)。给定与每一个课题相对应的Ai和Bi的值,请帮助Matrix67计算出如何选择论文的课题使得他可以花费最少的时间完成这n篇论文。

    Input

      第一行有两个用空格隔开的正整数n和m,分别代表需要完成的论文数和可供选择的课题数。

      以下m行每行有两个用空格隔开的正整数。其中,第i行的两个数分别代表与第i个课题相对应的时间系数Ai和指数Bi。

    Output

      输出完成n篇论文所需要耗费的最少时间。

    Sample Input

    10 3
    2 1
    1 2
    2 1

    Sample Output

    19

     Hint

    对于30%的数据,n<=10,m<=5;
    
    对于100%的数据,n<=200,m<=20,Ai<=100,Bi<=5

    Solution

      第一次定义的方程为f[i]表示写前i篇论文的最小花费,发现无法转移。因为a*(x+y)^b显然不等于a*x^b+a*y^b。考虑阶段划分:对于同一种课题的花费,在一个状态中要一次性计算下来,否则就会出现无法转移的情况。由此可将课题种类数作为阶段,设f[i][j]表示用前i个课题写j篇论文的花费。转移显然:f[i][j]=min{f[i-1][k]+a[i]*pow((j-k),b[i])|其中k<j}。显然可以把第一维滚动掉,但是我懒得滚了= =。

      对于边界,f[i][0]=0,其中i∈[0,m]

    Code

    #include<cstdio>
    #include<cstring>
    #define maxn 205
    #define maxm 25
    #define ll long long int
    
    inline void qr(ll &x) {
        char ch=getchar();ll f=1;
        while(ch>'9'||ch<'0')    {
            if(ch=='-')    f=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')    x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
        x*=f;
        return;
    }
    
    inline ll max(ll a,ll b) {return a>b?a:b;}
    inline ll min(ll a,ll b) {return a<b?a:b;}
    
    inline void swap(ll &a,ll &b) {
        ll c=a;a=b;b=c;return;
    }
    
    ll n,m,frog[maxn][maxn];
    
    struct Pa {
        ll a,b;
    };
    Pa pa[maxn];
    
    ll pow(ll a,ll b) {
        if(!b)    return 1;
        if(!(b^1))    return a;
        ll t=b/2;
        ll an=pow(a,t);
        if(b%2)    return an*an*a;
        else return an*an;
    }
    
    int main() {
        qr(n);qr(m);
        for(int i=1;i<=m;++i) {
            qr(pa[i].a);qr(pa[i].b);
        }
        std::memset(frog,0x3f,sizeof frog);frog[0][0]=0;
        for(int i=1;i<=m;++i)    frog[i][0]=0;
        for(int i=1;i<=m;++i) {
            for(int j=1;j<=n;++j) {
                for(int k=0;k<=j;++k) {
                    frog[i][j]=min(frog[i][j],frog[i-1][j-k]+pa[i].a*pow(k,pa[i].b));
                }
            }
        }
        printf("%lld
    ",frog[m][n]);
        return 0;
    }

    Summary

    1、不要闲的没事一上来就压维。发现状态不对容易懵逼

    2、在设计状态的时候,一般而言需要在一个状态中需要一次性计算的会被作为阶段进行划分,划分时注意感性领悟一下无后效性原则,不要闷头方程。

    3、在不压维状态下若以一个维度为阶段无法转移可以尝试使用另一个维度作为阶段

    4、对于看起来需要记录以往信息但又明显不是状压的DP(例如本题若使用论文数作为阶段需要记录对于每一个状态每一个课题选了多少),一般而言会把需要记录的信息作为阶段,目的还是,方便一次性计算。

    5、初始化的时候把所有能想到的边界全部初始化掉。别嫌麻烦。不然容易GG。

    End on 2018/6/3

  • 相关阅读:
    代码仓库
    介绍
    创建mysql数据库的命令
    操作流程
    Ubuntu20.04下Mercurial的安装与配置
    邮件列表-OpenJDK
    代码约定--OpenJDK
    ubuntu20.04 下 ADB调试android工具的安装
    openjdk开发者指南
    verifying module: xxx: initializing sumdb.Client: reading tree note: malformed note 解决方案
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/9130258.html
Copyright © 2011-2022 走看看