资源类动规练习

本文介绍了多个资源类动态规划问题,包括机器分配、最少硬币找零、系统可靠性、马棚问题、资源分配、复制书稿、乘积最大化、数字游戏、添括号问题、车队过桥和快餐问题等,探讨了解决这些问题的动态规划思路和方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

总结:资源类动规一般转移方程为f[i][j]=max{f[i-1][k]+s[k+1][j] 即考虑第i个阶段和第i-1个阶段的关系

          最外层循环起始位置一般从2开始,避免出现把K个资源分配给0个对象的错误

           对于f[1][j]要手动赋初值

          注意循环的上下界,初始化等


1、机器分配(machine.pasmachine.in  machine.out)

问题描述:

总公司拥有高效生产设备M台,准备分给下属的N个公司。各分公司若获得这些设备,可以为国家提供一定的盈利。问:如何分配这M台设备才能使国家得到的盈利最大?求出最大盈利值。其中M《=15,N〈=10。分配原则:每个公司有权获得任意数目的设备,但总台数不得超过总设备数M。保存数据的文件名从键盘输入。

数据文件格式为:第一行保存两个数,第一个数是设备台数M,第二个数是分公司数N。接下来是一个(M+1)*(N+1)的矩阵,A[2,2]表明了第1个公司分配1台机器的盈利。见样例

输出最大盈利值。

machine.in

5 3

0 0  0   0

1 3  5   4

2 7  11  6

3 9  11  11

4 12  11 12

5 13  11 12

machine.out

22

【思路】基本的资源类动规,f[i][j]表示前I个公司获得j台设备的最优值,转移方程见代码

#include<cstdio>
int n,m,f[20][20],i,j,v[20][20],k;
int maxx(int &a,int b) {if (a<b) a=b;}

int main()
{
   freopen("machine.in","r",stdin);
   freopen("machine.out","w",stdout);
   scanf("%d%d",&m,&n);
   for (i=0;i<=m;i++)
    for (j=0;j<=n;j++)
     scanf("%d",&v[i][j]);
   for (i=1;i<=n;i++)
     for (j=1;j<=m;j++)
       for (k=0;k<=j;k++)
         maxx(f[i][j],f[i-1][k]+v[j-k][i]);
    printf("%d\n",f[n][m]);
}     


2、最少硬币找钱问题(coin.pas)

设有n(1<=n<=100)种不同面值的硬币,各硬币的面值存于数组t[1..n]中,现要用这些面值的硬币来找钱,可以使用的各种面值的硬币个数不限,请计算找出钱数j(1<=j<=10000)的最少硬币个数。

输入文件:coin.in

第一行两个数,为N,J

第二行有N个数,为N种硬币的面值

输出文件:coin.out

只有一行,为最少硬币个数

样例输入:

2 5

1 2

样例输出:

3

【思路】完全背包

#include<cstdio>
int n,i,j,k,s;
int t[100],f[10001];

int main()
{
    freopen("coin.in","r",stdin);
    freopen("coin.out","w",stdout);
    scanf("%d%d",&n,&s);
    for (i=0;i<n;i++) scanf("%d",&t[i]);
    for (i=1;i<=s;i++) f[i]=30000;
    f[0]=0;
    for (i=0;i<n;i++)
     for (j=t[i];j<=s;j++)
       if (f[j-t[i]]+1<f[j]) f[j]=f[j-t[i]]+1;
    printf("%d\n",f[s]);
}
     


3、系统可靠性(xitong.pas xitong.in  xitong.out)

问题描述:

 一个系统由若干个部件串联而成,只要有一个部件故障,系统就不能正常运行,为提高系统的可靠性,每一不见都装有备用件,一旦原部件故障,备用件就自动进入系统。显然备用件越多,系统可靠性越高,但费用也越大,那么在一定总费用限制下,系统的最高可靠性等于多少?

设系统有n个部件,每个备用件的单价Ck,当部件K装置Mk个此备用件时部件的正常工作概率Pk(Mk),总费用上限C。求系统可能的最高可靠性。其中n<=50

输入:

2 20

3 0.6 0.65 0.70.75 0.8 0.85 0.9

5 0.7 0.75 0.80.8 0.9 0.95

输出:

0.6375

{第一行:n  C 
    第二行:C1 P1,0P1,1P1,X1 (0≤X1≤[C/Ck]) 
    … 
    n行:CnPn,0Pn,1Pn,X1 (0≤Xn≤[C/Cn]) 
[输出文件]:最大可靠性,保留 4位有效数字 }

感觉略题意不明啊

第一个代码是二维的比较好理解,第二个是一维优化

var n,c,cost,j,i,k:integer;
    p:array[0..100]of real;
	f:array[0..50,0..1000]of real;
	max:real;
begin
  assign(input,'xitong.in'); reset(input);
  assign(output,'xitong.out'); rewrite(output);
  readln(n,c);
  for i:=0 to n do
   for j:=0 to c do f[i][j]:=1;
  for i:=1 to n do
    begin
	  read(cost);
	  for j:=0 to c div cost do read(p[j]);
      for j:=0 to c do
        begin
            max:=0;
            for k:=0 to j div cost do
	      if f[i-1][j-cost*k]*p[k]>max then max:=f[i-1][j-cost*k]*p[k];
            f[i][j]:=max;
	   end;
    end;
	writeln(f[n][c]:0:4);
        close(input); close(output);
end.
	

var n,c,cost,j,i,k:integer;
    f,p:array[0..1000]of real;
	max:real;
begin
  assign(input,'xitong.in'); reset(input);
  assign(output,'xitong.out'); rewrite(output);
  readln(n,c);
  for i:=0 to c do f[i]:=1;
  for i:=1 to n do
    begin
	  read(cost);
	  for j:=0 to c div cost do read(p[j]);
          for j:=c downto 0 do
            begin
              max:=0;
              for k:=0 to j div cost do
			if f[j-cost*k]*p[k]>max then max:=f[j-cost*k]*p[k];
              f[j]:=max;
	   end;
    end;
	writeln(f[c]:0:4);
        close(input); close(output);
end.
	

4、马棚问题(stable.pas)

{问题描述}

每天,小明和他的马外出,然后他们一边跑一边玩耍。当他们结束的时候,必须带所有的马返回马棚,小明有K个马棚。他把他的马排成一排然后跟随他走向马棚。因为他们非常疲倦,小明不想让他的马做过多的移动。因此他想了一个办法:将马按照顺序放在马棚中,后面的马放的马棚的序号不会大于前面的马放的马棚的序号。而且他不想让他的K个马棚中任何一个空着,也不想让任何一匹马在外面。已知有黑白两种马,而且它们相处的很不融洽。如果有i个白马和j个黑马在一个马棚中,那么这个马棚的不愉快系数是i*j。所有k个马棚不愉快系数的和就是系数总和。确定一种方法把n匹马放入k个马棚,使得系数总和最小。     {输入格式}(stable.in)

第一行有两个数字:n(1〈=n<=500〉K(1〈=K〈=N〉。在接下来N行N个数。在这些行I行代表队列中的第I匹马的颜色:1意味着马是黑色,0意味着马是白色。

(输出格式)(stable.out)

一个数据,代表系数总和可能达到的最小值。

(输入样列)

6  3      //6匹马3个马棚

1                                   //第一匹马为黑马

1

0                                   //第3匹马为白马

1

0

1

(输出样例)

2                                   //最小系数总和

#include<cstdio>
#include<climits>

long f[501][501];
int white[501],black[501];
int i,j,n,m;

int main()
{
    freopen("stable.in","r",stdin);
    freopen("stable.out","w",stdout);
    scanf("%d%d\n",&n,&m);
    int x;
    white[0]=0; black[0]=0;
    for (i=1;i<=n;i++){
        scanf("%d",&x);
        white[i]=white[i-1];  black[i]=black[i-1];   
        if (x==1) black[i]++; else white[i]++;
        }
    f[0][0]=0;
    for (i=1;i<=n;i++) f[i][1]=white[i]*black[i];//处理一个马棚的特殊情况,若无会出现把X匹马装到0个马棚里 
    for (j=2;j<=m;j++)
     for (i=1;i<=n;i++)
     
     {
       long min=LONG_MAX;  
       for (int k=j-1;k<=i-1;k++)
         if (f[k][j-1]+(white[i]-white[k])*(black[i]-black[k])<min)
           min=f[k][j-1]+(white[i]-white[k])*(black[i]-black[k]);
       f[i][j]=min;
       }
     printf("%ld\n",f[n][m]);
     }        

5、资源分配(assigned.pas)

问题描述:

总公司拥有高效生产设备M台,准备分给下属的N个公司。各分公司若获得这些设备,可以为国家提供一定的盈利。问:如何分配这M台设备才能使国家得到的盈利最大?求出最大盈利值。其中M《=15,N〈=10。分配原则:每个公司有权获得任意数目的设备,但总台数不得超过总设备数M。

输入格式:

第一行保存两个数,第一个数是分公司数m,第二个数是设备台数n。接下来是一个M*N的矩阵,表明了第I个机器分给J个公司的盈利。

输出格式:

第一行为最大盈利值

第二~n+1 行为每个公司的分配方案

[输入样例](assignen.in)

3                                   3

30       40  50

20 30  50 

20       25  30

[输出样例](assigned.out)

70

1  1

2  1

3  1

#include<cstdio>
long n,m,f[20][20],v[20][20],i,j,k,mark[20][20];

void MAXX(long &a,long b){ 
     if (a<b) {
     a=b; 
     mark[i][j]=k;
     }
}

void search(long x,long y)
{
    if (x==0) return;
    int k=mark[x][y];
    search(x-1,k);//由K处转移而来  
    printf("%ld %ld\n",x,y-k);
} //从后往前找答案 

int main()
{
    freopen("assigned.in","r",stdin);
  freopen("assigned.out","w",stdout);
    scanf("%ld%ld",&m,&n);
    for (i=1;i<=m;i++)
      for (j=1;j<=n;j++) scanf("%d",&v[i][j]);
      for (i=1;i<=m;i++) //f[i][j]表示分配到第i个公司、第j台设备所获得的最大值 
       for (j=1;j<=n;j++)
        for (k=0;k<=j;k++)
          MAXX(f[i][j],f[i-1][k]+v[i][j-k]);
        
    printf("%ld\n",f[m][n]);
   search(m,n);
    //getchar();getchar();
}

6、 复制书稿(book.pas)

【问题描述】

    现在要把m本有顺序的书分给k给人复制(抄写),每一个人的抄写速度都一样,一本书不允许给两个(或以上)的人抄写,分给每一个人的书,必须是连续的,比如不能把第一、第三、第四本书给同一个人抄写。

    现在请你设计一种方案,使得复制时间最短。复制时间为抄写页数最多的人用去的时间。

【输入】

    第一行两个整数m,k;(k≤m≤500)

    第二行m个整数,第i个整数表示第i本书的页数。

【输出】

    共k行,每行两个整数,第i行表示第i个人抄写的书的起始编号和终止编号。k行的起始编号应该从小到大排列,如果有多解,则尽可能让前面的人少抄写。

【样例】

 book.in                                         

    93                                                  

    12 3 4 5 6 7 8 9  

 book.out

   1  5

   6  7

8  9                            

见http://blog.youkuaiyun.com/kemlkyo/article/details/19011929


7、乘积最大 (chen.pas)

问题描述:

今年是国际数学联盟确定的“2000——世界数学年”,又恰逢我国著名数学家华罗庚先生诞辰90周年。在华罗庚先生的家乡江苏金坛,组织了一场别开生面的数学智力竞赛的活动,你的一个好朋友XZ也有幸得以参加。活动中,主持人给所有参加活动的选手出了这样一道题目:

设有一个长度N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积能够为最大。

同时,为了帮助选手能够正确理解题意,主持人还举了如下的一个例子:

有一个数字串: 312,当N=3,K=1时会有以下两种分法:
1)3*12=36
2)31*2=62

这时,符合题目要求的结果是: 31*2=62

现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。

输入:(chen.in)
程序的输入共有两行:
第一行共有2个自然数N,K (6<=N<=40,1<=K<=6)
第二行是一个长度为N的数字串。

输出:(chen.out)

结果显示在屏幕上,相对于输入,应输出所求得的最大乘积(一个自然数)。

样例:

输入
4 2
1231

输出
62


【思路】高精度不能更恶心= =代码很丑

#include<cstdio>


struct mlong{
       int  x[50],len;
       }f[41][7];

mlong tmp;
mlong  g[41][41]={0};

int min( int x,int y) { if (x<y) return x; else return y; }


void gjc(const mlong a,const mlong b)//高精乘 
{
    int lena=a.len,lenb=b.len,ii,jj;
    for (ii=1;ii<=lena+lenb+1;ii++) tmp.x[ii]=0;
    for (ii=1;ii<=lena;ii++)
         for (jj=1;jj<=lenb;jj++)
          {
             tmp.x[ii+jj-1]+=a.x[ii]*b.x[jj];
             tmp.x[ii+jj]+=tmp.x[ii+jj-1]/10;
             tmp.x[ii+jj-1]%=10;
             }
    int lens=lena+lenb+1;
    while (!tmp.x[lens] && lens>1) --lens;
    tmp.len=lens;
}

bool check(const mlong a,const mlong b) //比较高精大小 
{
     if (a.len>b.len) return true;
     if (a.len<b.len) return false;
     int i=a.len;
     while (i)
     {
       if (a.x[i]<b.x[i]) return false;
       if (a.x[i]>b.x[i]) return true; //相等才继续比较 
       i--;
       }
     }            
    

int n,k,u,i,j;
char s[41],ss[41];


int main()
{
    
    freopen("chen.in","r",stdin);
    freopen("chen.out","w",stdout);
    scanf("%d%d",&n,&k);
    scanf("%s",&ss);
    for (i=1;i<=n;i++)
     s[i]=ss[n-i];
    for (i=1;i<=n;i++)
    {
       for (j=i;j<=n;j++)
         {
            if (i==j) 
               g[i][j].x[++g[i][j].len]=s[i]-'0';
              else 
              {
                g[i][j]=g[i][j-1];
                g[i][j].x[++g[i][j].len]=s[j]-'0';
                } 
         }
       f[i][0]=g[1][i];
    }       
  for (i=2;i<=n;i++)//f[i][j]前i位用J个乘号 
      for (j=1;j<=min(i-1,k);j++) //min(i-1,k) 
        for (u=j;u<=i-1;u++)
        {  
          gjc(f[u][j-1],g[u+1][i]);
          if (check(tmp,f[i][j])) f[i][j]=tmp;
         // for (int w=f[i][j].len;w>0;w--) printf("%d",f[i][j].x[w]);
          //printf("\n");
          }
   for (i=f[n][k].len;i>0;i--) printf("%d",f[n][k].x[i]);
   printf("\n");
   //getchar();getchar();
}       



8、数字游戏(Game.pas)

【问题描述】丁丁最近沉迷于一个数字游戏之中。这个游戏看似简单,但丁丁在研究了许多天之后却发觉原来在简单的规则下想要赢得这个游戏并不那么容易。游戏是这样的,在你面前有一圈整数(一共n个),你要按顺序将其分为m个部分,各部分内的数字相加,相加所得的m个结果对10取模后再相乘,最终得到一个数k。游戏的要求是使你所得的k最大或者最小。

例如,对于下面这圈数字(n=4,m=2):

2

 

 

4

 

-1

 

3

 

 

 

 

 

 


当要求最小值时,((2-1) mod 10)×((4+3)mod 10)=1×7=7,要求最大值时,为((2+4+3)mod 10)×(-1 mod 10)=9×9=81。特别值得注意的是,无论是负数还是正数,对10取模的结果均为非负值。

丁丁请你编写程序帮他赢得这个游戏。

 

【输入格式】输入文件第一行有两个整数,n(1≤n≤50)和m(1≤m≤9)。以下n行每行有个整数,其绝对值不大于104,按顺序给出圈中的数字,首尾相接。

 

【输出格式】输出文件有两行,各包含一个非负整数。第一行是你程序得到的最小值,第二行是最大值。

【输入样例】

4 2

4

3

-1

2

【输出样例】

7

81

9、添括号问题(add.pas)

 

一、试题

有一个由数字1,2,... ,9组成的数字串(长度不超过200),问如何将M(M<=20)个加号("+")插入到这个数字串中,使所形成的算术表达式的值最小。请编一个程序解决这个问题。

注意:加号不能加在数字串的最前面或最末尾,也不应有两个或两个以上的加号相邻。M保证小于数字串的长度。

例如:数字串79846,若需要加入两个加号,则最佳方案为79+8+46,算术表达式的值133。

[输入格式](add.in)

从键盘读入输入文件名。数字串在输入文件的第一行行首(数字串中间无空格且不折行),M的值在输入文件的第二行行首。

[输出格式](add.out)

在屏幕上输出所求得的最小和的精确值。

输入示例

82363983742

3

输出示例

2170

 

                                    10、车队过桥(bridge.pas)

〖题目描述〗

GDOI工作组遇到了一个运输货物的问题。现在有 N 辆车要按顺序通过一个单向的小桥,

由于小桥太窄,不能有两辆车并排通过,所以在桥上不能超车。另外,由于小桥建造的时间

已经很久,所以小桥只能承受有限的重量,记为 Max(吨)。所以,车辆在过桥的时候必须

要有管理员控制,将这N辆车按初始顺序分组,每次让一个组过桥,并且只有在一个组中所有的车辆全部过桥以后才让下一组车辆上桥。现在,每辆车的重量和最大速度是已知的,而每组车的过桥时间由该组中速度最慢的那辆车决定。现在请你编一个程序,将这 N辆车分组,使得全部车辆通过小桥的时间最短。

【输入格式】

数据存放在当前目录下的文本文件“bridge.in”中。

文件的第一行有三个数,分别为Max(吨),Len(桥的长度,单位:Km),N(三个数之间

用一个或多个空格分开)。 其中(max〈=500,len〈=100,N〈=20 〉

接下来有N行,每行两个数,第i行的两个数分别表示第i辆车的重量(吨)和最大速度(m/s)。

注意:所有的输入都为整数,并且任何一辆车的重量都不会超过 Max。

【输出格式】

答案输出到当前目录下的文本文件“bridge.out”中。

文件只有一行,输出全部车辆通过小桥的最短时间(s),精确到小数点后一位。

【输入输出样例】

bridge.in       bridge.out

100  5  9    1050.0

40  25 

50  20 

70 10 

12  50 

9  70

49  30

38  25 

27  50 

19  70

11、快餐问题(snack.pas/c/cpp)

【题目描述】

Peter最近在R市开了一家快餐店,为了招揽顾客,该快餐店准备推出一种套餐,该套餐由A个汉堡,B个薯条和C个饮料组成。价格便宜。为了提高产量,Peter从著名的麦当劳公司引进了N条生产线。所有的生产线都可以生产汉堡,薯条和饮料,由于每条生产线每天所能提供的生产时间是有限的、不同的,而汉堡,薯条和饮料的单位生产时间又不同。这使得Peter很为难,不知道如何安排生产才能使一天中生产的套餐产量最大。请你编一程序,计算一天中套餐的最大生产量。为简单起见,假设汉堡、薯条和饮料的日产量不超过100个。

【输入数据】

第一行为三个不超过100的正整数A、B、C中间以一个空格分开。

第二行为3个不超过100的正整数p1,p2,p3分别为汉堡,薯条和饮料的单位生产耗时。中间以一个空格分开。

第三行为为一个整数N (0<=0<=10),表示有N条流水线

第四行为N个不超过10000的正整数,其中Ti表示第i条生产流水线每天提供的生产时间,中间以一个空格分开。

【输出数据】:

仅一行,即每天套餐的最大产量。

【样例】

输入文件:snack.in

2 2 2

1 2 2

2

6 6

输出文件:snack.out

1

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值