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

最长公共子序列

【问题描述】给定两个字符串text1和text2,返回这两个字符串的最长公共子序列的长度。如果不存在公共子序列,则返回0。一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

【输入形式】输入的第1行中有一个字符串,表示text1;输入的第2行中有一个字符串,表示text2。
【输出形式】输出1行一个整数,表示text1和text2的最长公共子序列的长度。
【样例输入】

abcde

ace
【样例输出】

3
【样例说明】

最长公共子序列是ace,长度为3
【说明】

1<=text1.length, text2.length<=1000

text1和text2仅有小写英文字符组成。

#include<bits/stdc++.h>
using namespace std;
string a,b;
//求长度
int LCS_length(string a,string b)
{
    int n=a.length(),m=b.length();
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(a[i-1]==b[j-1]) dp[i][j]=dp[i-1][j-1]+1;
            else dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
        }
    }
    return dp[n][m];
}
//求序列
void LCS(string a,string b,int i,int j)
{
    if(i==0||j==0) return;
    if(a[i-1]==b[j-1])
    {
        LCS(a,b,i-1,j-1);
        cout<<a[i-1];
    }
    else if(dp[i-1][j]>dp[i][j-1]) LCS(a,b,i-1,j);
    else LCS(a,b,i,j-1);
}
int main()
{
    cin>>a>>b;
    cout<<LCS_length(a,b)<<endl;
    LCS(a,b,a.length(),b.length());
    return 0;
}

矩阵链相乘

【问题描述】给定n个矩阵M1,M2...Mn,他们的维数分别是r1*c1,r2*c2...rn*cn,要求使用【动态规划】的策略求解矩阵连乘的最优计算代价(总乘法次数最少)。题目保证矩阵相乘一定是有效的。

例如有三个矩阵M1,M2,M3,他们的维度分别是2*10,10*2,2*10。按照矩阵乘法的结合律,可以先把M1和M2相乘,然后把结果和M3相乘,总的乘法次数为2*10*2+2*2*10=80次;也可以先把M2和M3相乘,再用M1去相乘,这种方式下总的乘法次数为10*2*10+2*10*10=400次。因此最优计算代价为80。
【输入形式】输入的第1行中有1个数字n,表示矩阵的个数;接下来n行,每行2个整数ri和ci,分别表示矩阵Mi的行数和列数。
【输出形式】输出1行中有一个数字,表示n个矩阵相乘的最优计算代价。
【样例输入】

3

2 10

10 2

2 10
【样例输出】

80
【说明】

n>=2

1<=ri,ci<=20

d=1d=2d=3d=4d=5
m(1,1)m(1,2)m(1,3)m(1,4)m(1,5)
m(2,2)m(2,3)m(2,4)m(2,5)
m(3,3)m(3,4)m(3,5)
m(4,4)m(4,5)
m(5,5)
#include<bits/stdc++.h>
using namespace std;
int n,a[1000];
int m[1000][1000];//从矩阵i乘到矩阵j的最小数乘次数
int s[1000][1000];//i到j之间哪个中转站使m(i,j)最小
void Print(int i,int j)
{
    if(i==j) cout<<'M'<<i;
    else
    {
        cout<<'(';
        Print(i,s[i][j]);
        Print(s[i][j],j);
        cout<<')';
    }
}
int main()
{
    cin>>n;
    for(int i=0;i<2*n;i++)
    {
        int x;
        cin>>x;
        a[i/2+1]=x;
    }
    for(int d=2;d<=n;d++)//d个矩阵相乘
    {
        for(int i=1;i<=n-d+1;i++)//斜着有i个m(i,j)
        {
            int j=i+d-1;
            m[i][j]=9999;
            for(int k=i;k<=i+d-2;k++)//从i到j中间k位置断开
            {
                int temp=m[i][k]+m[k+1][j]+a[i]*a[k+1]*a[j+1];
                if(temp<m[i][j])
                {
                    m[i][j]=temp;
                    s[i][j]=k;
                }
            }
        }
    }
    cout<<m[1][n]<<endl;
    Print(1,n);
    return 0;
}
    

所有点对的最短路径问题

【问题描述】给定一个非负的加权有向图G,求其中任意两个节点之间的最短路径。
【输入形式】输入的第1行包含2个整数n和m,表示图G中包含n个节点和m条边。接下来m行,每行中有3个整数i,j,w,表示从节点i到节点j存在一条边(节点编号从1开始),该边的权重为w。
【输出形式】

输出最短路径矩阵,共包含n行,每行包含n个数字(数字之间使用空格分割),表示该节点到其他节点的最短距离。

特别地,节点到自身的距离定义为0;如果节点之间无法到达,使用1e9+7表示他们之间的距离。
【样例输入】

3 5

1 2 2

1 3 9

2 1 8

2 3 6

3 1 1
【样例输出】

0 2 8

7 0 6

1 3 0
【思考】如果出现负边,如何进行改进

#include<bits/stdc++.h>
using namespace std;
int n,m;
int dist[1000][1000];
int main()
{
   cin>>n>>m;
   memset(dist,0x3f3f3f3f,sizeof(dist));
   for(int i=1;i<=n;i++)
   {
      dist[i][i]=0;
   }
   for(int i=1;i<=m;i++)
   {
      int x,y,w;
      cin>>x>>y>>w;
      dist[x][y]=w;
   }
   for(int i=1;i<=n;i++)
   {
      for(int j=1;j<=n;j++)
      {
         for(int k=1;k<=n;k++)
         {
            dist[j][k]=min(dist[j][k],dist[j][i]+dist[i][k]);
         }
      }
   }
   for(int i=1;i<=n;i++)
   {
      for(int j=1;j<=n;j++)
      {
         cout<<dist[i][j]<<" ";
      }
      cout<<endl;
   }
   return 0;
}

01背包问题

【问题描述】给定一个容量为C的背包,现有n个物品,每个物品的体积分别为s1,s2...sn,价值分别为v1,v2...vn。每个物品只能放入一次。背包最多能装入价值为多少的物品。
【输入形式】输入的第1行包含2个整数C和n,分别表示背包容量和物品个数。接下来n行,每行包含2个整数si和vi,分别表示物品的体积和价值。
【输出形式】输出1行中含有一个数字,表示背包能装入的物品的最大价值。
【样例输入】

9 4

2 3

3 4

4 5

5 7
【样例输出】

12

#include<bits/stdc++.h>
using namespace std;
int c,n;
int s[1000],v[1000];
int dp[1000][1000];
int main()
{
   cin>>c>>n;
   for(int i=1;i<=n;i++) cin>>s[i]>>v[i];
   for(int i=1;i<=n;i++)//装入第i件物品
   {
      for(int j=1;j<=c;j++)//剩余体积
      {
         if(j>=s[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-s[i]]+v[i]);
         else dp[i][j]=dp[i-1][j];
      }
   }
   cout<<dp[n][c];
   return 0;
}

最少费用购物

问题描述:

  商店中每种商品都有标价。例如,一朵花的价格是2 元。一个花瓶的价格是5 元。为了吸引顾客,商店提供了一组优惠商品价。优惠商品是把一种或多种商品分成一组,并降价销售。例如,3 朵花的价格不是6 元而是5 元。2 个花瓶加1 朵花的优惠价是10 元。试设计一个算法,计算出某一顾客所购商品应付的最少费用。

编程任务:

  对于给定欲购商品的价格和数量,以及优惠商品价,编程计算所购商品应付的最少费用。

数据输入:

  欲购商品数据:第1 行中有1 个整数B(0≤B≤5),表示所购商品种类数。接下来的B 行,每行有3 个数C,K 和P。C 表示商品的编码(每种商品有唯一编码),1≤C≤999。K 表示购买该种商品总数,1≤K≤5。P 是该种商品的正常单价(每件商品的价格),1≤P≤999。请注意,一次最多可购买5*5=25 件商品。

  优惠商品价数据:第1 行中有1 个整数S(0≤S≤99),表示共有S 种优惠商品组合。接下来的S 行,每行的第一个数描述优惠商品组合中商品的种类数j。接着是j 个数字对(C,K),其中C 是商品编码,1≤C≤999。K 表示该种商品在此组合中的数量,1≤K≤5。每行最后一个数字P(1≤ P≤9999 )表示此商品组合的优惠价。

结果输出:

  程序运行结束时,输出所购商品应付的最少费用。

输入示例                                   输出示例

2                    2                           14

7 3 2             1 7 3 5

8 2 5             2 7 1 8 2 10

#include<bits/stdc++.h>
using namespace std;
struct com{
   int price;//优惠组合的价格
   int k[1000];//编号为j的商品数量
}zh[1000];//第i种优惠组合
int cost[6][6][6][6][6];
int num[1000];//商品编号
int p[1000];//编号为i的商品的单价
int k[1000];//编号为i的商品的需要数量
int b,s;//b种商品,s种组合
int main()
{
   //预购商品信息
   cin>>b;
   for(int i=1;i<=b;i++)
   {
      cin>>num[i];
      int temp=num[i];
      cin>>k[temp]>>p[temp];
   }
   
   //优惠组合信息
   cin>>s;
   for(int i=1;i<=s;i++)
   {
      int x;
      cin>>x;
      for(int j=1;j<=x;j++)
      {
         int tmp;
         cin>>tmp;
         cin>>zh[i].k[tmp];
      }
      cin>>zh[i].price;
   }

   cost[0][0][0][0][0]=0;
   for(int i=0;i<=k[num[1]];i++)
   {
      for(int j=0;j<=k[num[2]];j++)
      {
         for(int l=0;l<=k[num[3]];l++)
         {
            for(int m=0;m<=k[num[4]];m++)
            {
               for(int n=0;n<=k[num[5]];n++)
               {
                  //不使用优惠组合的价格
                  int minm=i*p[num[1]]+j*p[num[2]]+l*p[num[3]]+m*p[num[4]]+n*p[num[5]];
                  for(int g=1;g<=s;g++)//使用优惠组合
                  {
                  if(i<zh[g].k[num[1]]||j<zh[g].k[num[2]]||l<zh[g].k[num[3]]
                     ||m<zh[g].k[num[4]]||n<zh[g].k[num[5]])
                       continue;
                     int t=cost[i-zh[g].k[num[1]]][j-zh[g].k[num[2]]][l-zh[g].k[num[3]]]
                               [m-zh[g].k[num[4]]][n-zh[g].k[num[5]]]+zh[g].price;
                     if(t<minm) minm=t;
                  }
                  cost[i][j][l][m][n]=minm;
               }
            }
         }
      }
   }
   cout<<cost[k[num[1]]][k[num[2]]][k[num[3]]][k[num[4]]][k[num[5]]];
   return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值