[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思想还不完善,练习还太少,还需要更多的练习和总结。

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

原文地址:https://www.cnblogs.com/yangqingli/p/4803029.html