版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Ronaldo7_ZYB/article/details/89671708
题目描述
题解
这种比较特殊的 还是没有做到过唉。。
这道题我们观察数据,发现 的具体数字很小,但是背包的花费却很大,我们可以考虑用另外一张方式进行背包:用价值作为状态,用花费作为具体的 值进行计算。
求在体积限制为代价最大,等价于在相同的价值下体积最小:即用最小的体积买到最大的代价。
为了更加方便统计答案,我们对这些店进行排序:按照每一家店的时间来进行排序。
我们设
表示前i家店,购买出价值为
的最小花费,转移和正常的01背包相似:
表示价值,
表示花费。
这一步完成后,我们再改变一下状态:
- 设 表示前 个商店,价值至少为 的最小花费。
- 那么就有:
- 这样就保证了答案是单调不上升的。
那么最后的步骤,我们可以使用两边二分查找来进行:
- 第一遍:找到能够购买的最后一家店 。
- 第二遍:在 这个一位数组上进行二分查找。找到第一个比 要大的下表,然后再 ,这就是我们所要找的答案。
这种方法十分巧妙,有两个程序。第一个是使用 的,第二个是手写二分答案的。
代码1:
#include <bits/stdc++.h>
using namespace std;
int n,m;
int t[301];
int v[301];
long long c[301];
long long f[301][90001];
void Sort(void)
{
for (int i=1;i<n;++i)
for (int j=i+1;j<=n;++j)
if (t[i]>t[j])
{
swap(t[i],t[j]);
swap(v[i],v[j]);
swap(c[i],c[j]);
}
return;
}
int main(void)
{
freopen("market.in","r",stdin);
freopen("market.out","w",stdout);
scanf("%d %d",&n,&m);
for (int i=1;i<=n;++i)
scanf("%lld %d %d",c+i,v+i,t+i);
Sort();
int V = n*300;
for (int i=1;i<=V;++i) f[0][i] = 1e15;
for (int i=1;i<=n;++i)
for (int j=0;j<=V;++j)
{
f[i][j] = f[i-1][j];
if (j-v[i] >= 0) f[i][j] = min(f[i][j], f[i-1][j-v[i]]+c[i]);
}
for (int i=1;i<=n;++i)
for (int j=V-1;j>=0;--j)
f[i][j] = min(f[i][j+1], f[i][j]);
//f[i][j] 表示 前i件物品 价值至少为j的最小代价
for (int i=1,T,M;i<=m;++i)
{
scanf("%d %d",&T,&M);
int pos = upper_bound(t+1,t+n+1,T)-t-1;// T <= t[pos]
int ans = upper_bound(f[pos]+1,f[pos]+V+1,M)-f[pos]-1;// m <= f[pos][ans]
printf("%d\n",ans);
}
return 0;
}
代码2:
#include <bits/stdc++.h>
using namespace std;
int n,m;
int t[301];
int v[301];
long long c[301];
long long f[301][90001];
void Sort(void)
{
for (int i=1;i<n;++i)
for (int j=i+1;j<=n;++j)
if (t[i]>t[j])
{
swap(t[i],t[j]);
swap(v[i],v[j]);
swap(c[i],c[j]);
}
return;
}
int find(int x)
{
//找到时间小于等于x的最大值
int l = 1, r = n;
while (l <= r)
{
int mid = l+r >> 1;
if (x >= t[mid]) l = mid+1;
else r = mid-1;
}
if (x >= t[r]) return r;
else return l;
}
int find2(int t,int x)
{
//找到前t件物品中:花费小于等于x的最大价值
#define F(i) f[t][i]
int l = 1,r = n*300;
while (l <= r)
{
int mid = l+r >> 1;
if (x >= F(mid)) l = mid+1;
else r = mid-1;
}
if (x >= F(r)) return r;
else return l;
}
int main(void)
{
freopen("market.in","r",stdin);
freopen("market.out","w",stdout);
scanf("%d %d",&n,&m);
for (int i=1;i<=n;++i)
scanf("%lld %d %d",c+i,v+i,t+i);
Sort();
int V = n*300;
for (int i=1;i<=V;++i) f[0][i] = 1e15;
for (int i=1;i<=n;++i)
for (int j=0;j<=V;++j)
{
f[i][j] = f[i-1][j];
if (j-v[i] >= 0) f[i][j] = min(f[i][j], f[i-1][j-v[i]]+c[i]);
}
for (int i=1;i<=n;++i)
for (int j=V-1;j>=0;--j)
f[i][j] = min(f[i][j+1], f[i][j]);
//f[i][j] 表示 前i件物品 价值至少为j的最小代价
for (int i=1,T,M;i<=m;++i)
{
scanf("%d %d",&T,&M);
int pos = find(T);
int ans = find2(pos,M);// m <= f[pos][ans]
printf("%d\n",ans);
}
return 0;
}