[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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一道经典的字符串问题,可以使用动态规划(DP)来解决。 首先,我们需要一个数组 $f$ 来表示以第 $i$ 个字符串结尾的最长接龙长度。然后,我们可以通过枚举前一个字符串来计算 $f_i$: $$ f_i = \max_{j=1}^{i-1} \{ f_j+1 \} \ \text{if} \ s_j \text{是 s_i 的后缀} $$ 其,$s_i$ 表示第 $i$ 个字符串。 最后,我们只需要在数组 $f$ 找到最大值即可。 下面是代码实现: ### 回答2: codevs1051接龙游戏是一个基于编程的智力游戏。在这个游戏,玩家需要根据给定的单词,以尽可能长的序列依次接龙。 游戏开始时,系统会随机给出一个单词作为起始单词。玩家需要根据这个起始单词,提供一个合法的接龙单词。合法的接龙单词是以起始单词的最后一个字母作为开头的单词。例如,如果起始单词是“苹果”,那么玩家可以选择提供单词“鸭梨”作为接龙单词。 接龙单词提供后,系统会判断该单词是否符合规则。如果符合规则,系统会将该单词作为新的起始单词,并要求下一个玩家继续接龙。如果单词不符合规则,系统会要求玩家重新提供合法的接龙单词。 游戏进行的过程,玩家可以通过编程的方式实现自动接龙。可以使用字符串处理的方法,提取出单词的最后一个字母,并根据这个字母去查询字典,找到合适的接龙单词。 codevs1051接龙游戏不仅可以锻炼玩家的思维能力和逻辑推理能力,还能提高玩家的编程水平。通过编程实现自动接龙,不仅提高了游戏的趣味性,还能让玩家在实践学习编程知识。同时,这个游戏还可以增加玩家之间的交流和竞争,提高了玩家的团队合作和沟通能力。 总之,codevs1051接龙游戏是一款有趣的智力游戏,它通过编程的方式提升了游戏的难度和挑战性。无论是对于编程爱好者,还是普通玩家来说,这个游戏都能带来很多乐趣和学习的机会。 ### 回答3: CodeVS1051接龙游戏是一款基于编程思维的游戏。游戏规则很简单,给定一个单词列表,每个单词都由小写字母组成。玩家需要根据给定的单词,以最后一个字母相同的字母开头,选择一个单词。然后其他玩家继续以所选单词的最后一个字母作为起始字母进行选择。这样依次循环,直到某个玩家无法找到符合条件的单词为止。此时,该玩家输掉游戏。 在CodeVS1051接龙游戏,玩家需要通过编程实现自动选择单词的功能。首先,将给定的单词列表储存在一个数组。然后,由玩家输入一个初始单词作为起始点。接下来,编程需要根据规则自动选择下一个单词。可以通过遍历列表的单词,判断每个单词的首字母是否与上一个单词的最后一个字母相同,如果相同,则将其选择为下一个单词。若列表无符合条件的选项,则表示当前玩家输掉了游戏。 要实现这个功能,需要使用循环结构来遍历单词列表,并使用条件语句进行判断。在编程,可以使用字符串处理函数来获取单词的首字母和最后一个字母。还可以使用数组来保存玩家的选择,以便检查其合法性。 总之,CodeVS1051接龙游戏是一款简单而有趣的编程题目。通过编程能够实现自动选择单词的功能,提高编程思维和逻辑思维能力。希望大家在玩游戏的同时,也能够享受到编程的乐趣。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值