算法实验复习三 动态规划(二)

最短下降路径

【问题描述】

给定一个方形整数数组 A,我们想要得到通过 A 的下降路径的最小和。

下降路径可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列。
【输入形式】

第一行输入数组的大小n,第二行以后输入的n行分别是数组的第n行元素

【输出形式】

输出1行中含有一个数字,表示最短下降路径和。

【样例输入】

3

2 1 3

6 5 4

7 8 9
【样例输出】

13

#include<bits/stdc++.h>
using namespace std;
int n,a[1000][1000];
int main()
{
    cin>>n;
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            cin>>a[i][j];
        }
    }
    //从最后一行往前推
    for(int i=n-2;i>=0;i--)
    {
        for(int j=n-1;j>=0;j--)
        {
            if(j==n-1) a[i][j]=a[i][j]+min(a[i+1][j],a[i+1][j-1]);
            else if(j==0) a[i][j]=a[i][j]+min(a[i+1][j],a[i+1][j+1]);
            else a[i][j]=a[i][j]+min(min(a[i+1][j-1],a[i+1][j]),a[i+1][j+1]);
        }
    }
    int res=a[0][0];
    for(int i=1;i<n;i++)
    {
        if(a[0][i]<res) res=a[0][i];
    }
    cout<<res;
    return 0;
}

最少硬币问题

问题描述:

  设有n 种不同面值的硬币,各硬币的面值存于数组T[1:n ]中。现要用这些面值的硬币来找钱。可以使用的各种面值的硬币个数存于数组Coins[1:n ]中。对任意钱数0≤m≤20001,设计一个用最少硬币找钱m 的方法。

编程任务:

  对于给定的1≤n≤10,硬币面值数组T 和可以使用的各种面值的硬币个数数组Coins, 以及钱数m,0≤m≤20001,编程计算找钱m 的最少硬币数。

数据输入:

  第一行中只有1 个整数给出n 的值,第2 行起每行2 个数,分别是T[j] 和Coins[j] 。最后1 行是要找的钱数m。

结果输出:

  程序运行结束时,输出最少硬币数。问题无解时输出-1。

输入示例            输出示例

3                          5

1 3

2 3

5 3

18

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
int n,t[maxn],coins[maxn],dp[maxn],num;
//dp[i]=min(dp[i-t[j]]+1,dp[i]) i表示剩余的需要找钱的钱数
int main()
{
   scanf("%d",&n);
   for(int i=0;i<n;i++)
   {
      scanf("%d%d",&t[i],&coins[i]);
   }
   scanf("%d",&num);
   for(int i=1;i<=num;i++) dp[i]=maxn;
   for(int i=0;i<n;i++)//第i种面值
   {
      for(int j=1;j<=coins[i];j++)//第i种面值的硬币数
      {
         for(int k=num;k>=t[i];k--)//需要找钱的钱数
         {
            dp[k]=min(dp[k-t[i]]+1,dp[k]);
         }
      }
   }
   if(dp[num]==maxn) printf("-1");
   else printf("%d",dp[num]);
   return 0;
}

独立任务最优调度问题

问题描述:

    用2 台处理机A 和B 处理n 个作业。设第i 个作业交给机器A 处理时需要时间ai ,若由机器B 来处理,则需要时间bi。由于各作业的特点和机器的性能关系,很可能对于某些i, 有ai ≥ bi ,而对于某些j,j≠i,有aj < bj 。既不能将一个作业分开由2 台机器处理,也没有一台机器能同时处理2 个作业。设计一个动态规划算法,使得这2 台机器处理完这n 个作业的时间最短(从任何一台机器开工到最后一台机器停工的总时间)。研究一个实例:(a1,a2,a3,a4,a5,a6)=(2,5,7,10,5,2);(b1,b2,b3,b4,b5,b6)=(3,8,4,11,3,4) 。

编程任务:

    对于给定的2 台处理机A 和B 处理n 个作业,找出一个最优调度方案,使2 台机器处理完这n 个作业的时间最短。

数据输入:

    第1 行是1 个正整数n, 表示要处理n 个作业。接下来的2 行中,每行有n 个正整数,分别表示处理机A 和B 处理第i 个作业需要的处理时间。

结果输出:

  程序运行结束时,将输出最短处理时间。

输入文件示例输出文件示例

input.txt                       output.txt

6                                  15

2 5 7 10 5 2

3 8 4 11 3 4

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
//dp[k][x]=min(dp[k-1][x]+b[k],dp[k-1][x-a[k]])
//完成前k项任务,在a耗时x的前提下,b花费的最小时间
int n,a[maxn],b[maxn],Time[maxn],dp[maxn][maxn];
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    for(int i=1;i<=n;i++)
    {
        cin>>b[i];
    }
    int suma=a[1];
    for(int x=0;x<a[1];x++)//耗时比a[1]小,b来做
    {
        dp[1][x]=b[1];
    }
    dp[1][a[1]]=min(b[1],a[1]);//耗时等于a的完成时间,取a,b中小的一个
    //赋初值
    for(int i=2;i<=n;i++)
    {
        for(int j=0;j<=n;j++)
        {
            dp[i][j]=maxn;
        }
    }
    for(int k=2;k<=n;k++)
    {
        suma+=a[k];//a单独完成前k项任务的时间
        Time[k]=maxn;//完成前k项任务的最小时间
        for(int x=0;x<=suma;x++)
        {
            if(x<a[k])
            {
                dp[k][x]=dp[k-1][x]+b[k];
            }
            else
            {
                dp[k][x]=min(dp[k-1][x]+b[k],dp[k-1][x-a[k]]);
            }
            Time[k]=min(Time[k],max(x,dp[k][x]));
        }
    }
    cout<<Time[n];
    return 0;
}

双调旅行售货员问题

欧氏旅行售货员问题是对给定的平面上n 个点确定一条连接这n 个点的长度最短的哈密顿回路。由于欧氏距离满足三角不等式,所以欧氏旅行售货员问题是一个特殊的具有三角不等式性质的旅行售货员问题。它仍是一个NP 完全问题。最短双调TSP 回路是欧氏旅行售货员问题的特殊情况。平面上n 个点的双调TSP 回路是从最左点开始,严格地由左至右直到最右点,然后严格地由右至左直至最左点,且连接每一个点恰好一次的一条闭合回路。

编程任务:

  给定平面上n 个点,编程计算这n 个点的最短双调TSP 回路。

数据输入:

  第1 行有1 个正整数n,表示给定的平面上的点数。接下来的n 行中,每行2 个实数,分别表示点的x 坐标和y 坐标。

结果输出:

  输出计算的最短双调TSP 回路的长度(保留2 位小数) 。

输入示例              输出示例

7                            25.58

0 6

1 0

2 3

5 4

6 1

7 5

8 2

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
int n;
double x[maxn],y[maxn];
double p[maxn][maxn],d[maxn][maxn];//p[i][j]表示第i个点与第j个点的距离
double length(double x1,double y1,double x2,double y2)
{
    double a=(x1-x2)*(x1-x2);
    double b=(y1-y2)*(y1-y2);
    return sqrt(a+b);
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>x[i]>>y[i];
    }
    //按x坐标排序
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            if(x[i]>x[j])
            {
                swap(x[i],x[j]); swap(y[i],y[j]);
            }
        }
    }
    //计算两点间距离
    for(int i=1;i<=n;i++)
    {
        for(int j=i+1;j<=n;j++)
        {
            p[i][j]=p[j][i]=length(x[i],y[i],x[j],y[j]);
        }
    }
    d[1][1]=0;
    for(int i=2;i<n;i++)
    {
        d[i+1][i]=1000;//从第i+1个点到第一个点再回到第i个点的距离
        for(int j=1;j<=i-1;j++)
        {
            d[i+1][j]=d[i][j]+p[i][i+1];
            d[i+1][i]=min(d[i+1][i],d[i][j]+p[j][i+1]);
        }
    }
    cout<<fixed<<setprecision(2)<<d[n][n-1]+p[n][n-1]<<endl;
    return 0;
}

汽车加油行驶问题

给定一个N*N 的方形网格,设其左上角为起点◎,坐标为(1,1),X 轴向右为正,Y 轴向下为正,每个方格边长为1。一辆汽车从起点◎出发驶向右下角终点▲,其坐标为(N, N)。在若干个网格交叉点处,设置了油库,可供汽车在行驶途中加油。汽车在行驶过程中应遵守如下规则:

(1)汽车只能沿网格边行驶,装满油后能行驶K 条网格边。出发时汽车已装满油,在

起点与终点处不设油库。

(2)当汽车行驶经过一条网格边时,若其X 坐标或Y 坐标减小,则应付费用B,否则

免付费用。

(3)汽车在行驶过程中遇油库则应加满油并付加油费用A。

(4)在需要时可在网格点处增设油库,并付增设油库费用C(不含加油费用A)。

(5)(1)~(4) 中的各数N、K、A、B、C 均为正整数。

编程任务:

  求汽车从起点出发到达终点的一条所付费用最少的行驶路线。

数据输入:

  第一行是N,K,A,B,C 的值,2 ≤ N ≤ 100,2 ≤ K ≤ 10。第二行起是一个N*N 的0-1 方阵,每行N 个值,至N+1 行结束。方阵的第i 行第j 列处的值为1 表示在网格交叉点(i,j)处设置了一个油库,为0 时表示未设油库。各行相邻的2 个数以空格分隔。

结果输出:

  程序运行结束时,输出找到的最优行驶路线所需的费用。

输入示例                      输出示例

9 3 2 3 6                         12

0 0 0 0 1 0 0 0 0

0 0 0 1 0 1 1 0 0

1 0 1 0 0 0 0 1 0

0 0 0 0 0 1 0 0 1

1 0 0 1 0 0 1 0 0

0 1 0 0 0 0 0 1 0

0 0 0 0 1 0 0 0 1

1 0 0 1 0 0 0 1 0

0 1 0 0 0 0 0 0 0

#include<bits/stdc++.h>
using namespace std;
//dp(x,y,0) 表示(1,1)到(x,y)所需最少费用
//dp(x,y,1) 表示汽车行驶到(x,y)还能行驶的网格边数
//dp(1,1,0)=0 dp(1,1,1)=k
//dp(x,y,0)=min{dp(x+s[i][0],y+s[i][1],0)+s[i][2]}
//dp(x,y,1)=dp(x+s[i][0],y+s[i][1],1)-1
//dp(x,y,0)=dp(x,y,0)+a (x,y)是油库
//dp(x,y,0)=dp(x,y,0)+c+a (x,y)不是油库
int n,k,a,b,c;
int arr[1000][1000];
int dp[1000][1000][3];
int main()
{
   cin>>n>>k>>a>>b>>c;
   for(int i=1;i<=n;i++)
   {
       for(int j=1;j<=n;j++)
       {
           cin>>arr[i][j];
       }
   }

   //初始化
   for(int i=1;i<=n;i++)
   {
       for(int j=1;j<=n;j++)
       {
           dp[i][j][0]=9999;
           dp[i][j][1]=k;
       }
   }

   //四个方向
   int s[4][3]={{-1,0,0},{0,-1,0},{1,0,b},{0,1,b}};

   //起点值初始化
   dp[1][1][0]=0; dp[1][1][1]=k;

   for(int x=1;x<=n;x++)
   {
       for(int y=1;y<=n;y++)
       {
           if(x==1&&y==1) continue;
           int minMoney=9999;//(x,y)上一个位置的最小钱数
           int minStep;
           int tmpMoney,tmpStep;
           for(int i=0;i<=3;i++)//遍历上个位置的所有可能情况
           {
               if(x+s[i][0]<1||x+s[i][0]>n||y+s[i][1]<1||y+s[i][1]>n) continue;
               tmpMoney=dp[x+s[i][0]][y+s[i][1]][0]+s[i][2];
               tmpStep=dp[x+s[i][0]][y+s[i][1]][1]-1;
               if(arr[x][y]==1) { tmpMoney+=a; tmpStep=k;}
               if(arr[x][y]==0&&tmpStep==0&&(x!=n||y!=n)) { tmpMoney+=a+c; tmpStep=k;}
               if(minMoney>tmpMoney)//更新最小值
               {
                   minMoney=tmpMoney;
                   minStep=tmpStep;
               }
           }
           if(dp[x][y][0]>minMoney)
           {
               dp[x][y][0]=minMoney;
               dp[x][y][1]=minStep;
           }
       }
   }
   cout<<dp[n][n][0];
   return 0;
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值