学习总结:分组背包和二分查找

分组背包问题:

题目:
有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。这些物品被划分为若干组,每组中的物品互相冲突,最多选一件。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

算法:

每组物品有若干种策略::是选择本组的某一件,还是一件都不选。
即:先对每一组进行01背包的判断,再对每一组中的物品进行判断
状态方程:
f[k][v]=max{f[k-1][v],f[k-1][v-c[i]]+w[i]|物品i属于组k}//f[k][v]表示前k组物品花费费用v能取得的最大值 。

代码:

for(i=0;i<n;i++)//组数;
for(j=m;j>=0;j–)//体积;
for(k=1;k<=j;k++)//每组中的特定物品;
dp[j]=max(dp[j],dp[j-k]+a[i][k]);

例题:

给定n门课,再用m天复习这n门课,然后给定一个n*m的矩阵A,A[i][j]表示用j天复习第i门课获得得收益。问用m天复习不同的课获得的最大收益。
解:

#include<stdio.h>
#include<string.h>
int dp[105],a[105][105];
int max(int a,int b)
{
       if(a<b)
           return b;
       return a;
}
int main()
{
       int i,j,k,n,m;
       while(scanf("%d%d",&n,&m)!=EOF)
       {
              if(n==0&&m==0)
                  break;
              memset(dp,0,sizeof(dp));
              for(i=0;i<n;i++)
                for(j=1;j<=m;j++)
                  scanf("%d",&a[i][j]);
              for(i=0;i<n;i++)
                  for(j=m;j>=0;j--)
                     for(k=1;k<=j;k++)
                       dp[j]=max(dp[j],dp[j-k]+a[i][k]);
               printf("%d\n",dp[m]);
       }
       return 0;
}

二分查找:

定义:

在一个单调有序的集合中查找元素,每次将集合分为左右两部分,判断解在哪个部分中并调整集合上下界,重复直到找到目标元素。

特点:

(1)必须有序;
(2)时间复杂度要低于直接查找;
(3)利用二分查找求最大值看下限,求最小值看上限;
(4)不断更改上下限;

例题:

将n根网线切成k段相同长度的网线,问可切成的最长长度是多少;
解:

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
using namespace std;
#define exp 1e-8
 
int n,k;
double a[10005],sum;
 
int judge(double s)
{
    int cnt = 0;
    for(int i = 0; i<n; i++)
    {
        cnt+=(int)(a[i]/s);
    }
    if(cnt>=k) return 1;
    return 0;
}
 
int main()
{
    int i;
    while(~scanf("%d%d",&n,&k),n+k)
    {
        sum = 0;
        for(i = 0; i<n; i++)
        {
            scanf("%lf",&a[i]);
            sum+=a[i];
        }
        sum = sum/k;
        double l = 0,r = sum;
        while(fabs(l-r)>exp)
        {
            double mid = (l+r)/2;
            if(judge(mid))
                l = mid;
            else
                r = mid;
        }
        printf("%.2f\n",l);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值