zoukankan      html  css  js  c++  java
  • [codevs1554]最佳课题选择

    题目描述

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

    输入输出格式

    输入格式:

    第一行有两个用空格隔开的正整数n和m,分别代表需要完成的论文数和可供选择的课题数。 
    以下m行每行有两个用空格隔开的正整数。其中,第i行的两个数分别代表与第i个课题相对应的时间系数Ai和指数Bi。

    输出格式:

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

    输入输出样例

    输入样例#1:

    10 3
    2 1
    1 2
    2 1
    

    输出样例#1:

    19
    

    说明

    【样例说明】
    4篇论文选择课题一,5篇论文选择课题三,剩下一篇论文选择课题二,总耗时为2*4^1+1*1^2+2*5^1=8+1+10=19。可以证明,不存在更优的方案使耗时小于19。
    【数据规模与约定】
    对于30%的数据,n<=10,m<=5; 
    对于100%的数据,n<=200,m<=20,Ai<=100,Bi<=5。

    解题报告

    这个题目容易想到是一个背包,每一篇论文都有在当前课题下写或不写两个选择,预处理数组f[i,j]表示第i个课题写第j个论文时的花费,ff[i,j]:=min(ff[i-1,j],f[i,j-1])即可。

    后来研究了几组数据发现想的还是太少了,例如说对于样例数据的f[2,3]的值,结果不是6,9或许其他的什么,而是写两篇一课题,一篇二课题,而这样的话我的方程显然就不对了。

    于是我又机智的写了一个在30%数据内的搜索完成这项功能,代码如下,也确实是30分。

    var f:array[-1..200,-1..200] of int64;
        ff:array[-1..200,-1..3200] of int64;
        a:array[1..20] of int64;
        b:array[1..20] of int64;
        n,m,i,j,k:longint;
        minn:int64=100000000;
    
    function min(x,y:int64):int64;
    begin
        if x>y then exit(y) else exit(x);
    end;
    
    function mi(a,b:int64):int64;
    var t,y:int64;
    begin
        t:=1;
        y:=a;
        while b<>0 do
            begin
                if (b and 1)=1 then t:=t*y;
                y:=y*y;
                b:=b shr 1;
            end;
        exit(t);
    end;
    
    procedure five;
    var i,j,k,l,o:longint;
    begin
        for i:=0 to n do
            for j:=0 to n do
                for k:=0 to n do
                    for l:=0 to n do
                        for o:=0 to n do
                            if (i+j+k+l+o=n) then
                                minn:=min(minn,f[1,i]+f[2,j]+f[3,k]+f[4,l]+f[5,o]);
    end;
    
    procedure four;
    var i,j,k,l,o:longint;
    begin
        for i:=0 to n do
            for j:=0 to n do
                for k:=0 to n do
                    for l:=0 to n do
                            if (i+j+k+l=n) then
                                minn:=min(minn,f[1,i]+f[2,j]+f[3,k]+f[4,l]);
    end;
    
    procedure three;
    var i,j,k,l,o:longint;
    begin
        for i:=0 to n do
            for j:=0 to n do
                for k:=0 to n do
                            if (i+j+k=n) then
                                minn:=min(minn,f[1,i]+f[2,j]+f[3,k]);
    end;
    
    procedure two;
    var i,j,k,l,o:longint;
    begin
        for i:=0 to n do
            for j:=0 to n do
                            if (i+j=n) then
                                minn:=min(minn,f[1,i]+f[2,j]);
    end;
    
    procedure one;
    var i:longint;
    begin
        minn:=f[1,n];
    end;
    
    begin
        readln(n,m);
        fillchar(f,sizeof(f),0);
        fillchar(ff,sizeof(ff),0);
        for i:=1 to m do
            readln(a[i],b[i]);
        for i:=1 to m do
            for j:=0 to n do
                f[i,j]:=a[i]*mi(j,b[i]);
        if m=1 then one;
        if m=2 then two;
        if m=3 then three;
        if m=4 then four;
        if m=5 then five;
        if minn=100000000 then 
            begin
                for i:=1 to m do
                    for j:=0 to n do
                        ff[i,j]:=min(f[i,j-1],f[i-1,j]);
                minn:=f[m,n]
            end;
        writeln(minn);
    end.
    View Code

    其实实际上也就是一个背包,再多考虑一种循环,表示当现在一共写了j篇论文时,用k篇是在当前课题下写的,这样就可以考虑到所有的情况了,代码如下:

    {AC}
    var f:array[-1..200,-1..200] of int64;
    //前i个课题写了j个论文的最小花费
        a:array[1..20] of int64;  
        b:array[1..20] of int64;  
        n,m,i,j,k:longint;  
    
    function min(x,y:int64):int64;  
    begin  
        if x>y then exit(y) else exit(x);  
    end;  
      
    function mi(a,b:int64):int64;  
    var t,y:int64;  
    begin  
        t:=1;  
        y:=a;  
        while b<>0 do  
            begin  
                if (b and 1)=1 then t:=t*y;  
                y:=y*y;  
                b:=b shr 1;  
            end;  
        exit(t);  
    end;  
    //快速幂,其实对于这道题的数据加不加用处不大
    
    begin  
        readln(n,m);  
        fillchar(f,sizeof(f),0);  
        for i:=1 to m do  
            readln(a[i],b[i]);  
        for i:=1 to n do f[i,0]:=0;  
        for i:=1 to n do  
            f[1,i]:=a[1]*mi(i,b[1]);  
        //预处理最底层的元素
        for i:=2 to m do  //一共选了多少课题
            for j:=1 to n do  //一共写了多少论文
                begin  
                    f[i,j]:=f[i-1,j];  //一定要先赋值,否则可能得出不正确答案
                    for k:=1 to j do  //在当前课题下写了多少论文
                        f[i,j]:=min(f[i,j],f[i-1,j-k]+a[i]*mi(k,b[i]));  
          end;  
        writeln(f[m,n]);  
    end.  
    View Code

    这次的惨痛教训表明我的DP思想还不完善,练习还太少,还需要更多的练习和总结。

    加油,争取拿出联赛一等!

  • 相关阅读:
    Liberty Mutual Property Inspection, Winner's Interview: Qingchen Wang
    均方根值(RMS)+ 均方根误差(RMSE)+标准差(Standard Deviation)
    Comparing Differently Trained Models
    Stochastic Optimization Techniques
    Here’s just a fraction of what you can do with linear algebra
    14种机器学习常见算法分类汇总
    高速充电技术介绍
    javacc学习总结
    组合查询(机房重构知识点总结)
    Linux下vi编辑器的使用
  • 原文地址:https://www.cnblogs.com/yangqingli/p/4803029.html
Copyright © 2011-2022 走看看