分组背包问题:
题目:
有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;
}