每日一题 P3092 [USACO13NOV]No Change G 状态压缩+二分
这是一道和 [P3694 邦邦的大合唱站队] 非常相似的蓝色题目,前面一题刚开始不太会做,看了题解开导思路,这道题自己独立做出来了。
dp[i]的下标i代表已经用了的硬币,值代表买到的最后一样东西。转移方程就是枚举比i中少一个1的状态转移到i状态,在那个状态的值往后二分搜出当前硬币能往后买到的最后面的物品编号,然后取最大值。当当前的状态可以买到所有物品后,更新答案ans。ans就是没有取的硬币的和。
#include <bits/stdc++.h>
#define endl "\n"
#define MAXN 5000005
using namespace std;
int n,m;
int val[MAXN];
int dp[1<<17];
int ans=-1;
int coin[17],sum;
int get(int pos,int v)
{
int L=pos,R=m;
while(L<R)
{
int mid=(L+R+1)>>1;
if(val[mid]-val[pos]<=v)
L=mid;
else
R=mid-1;
}
return L;
}
void update(int x)
{
int ret=sum;
for(int i=0;i<17;i++)
if(x&(1<<i))
{
ret-=coin[i+1];
int f=x&~(1<<i);
dp[x]=max(get(dp[f],coin[i+1]),dp[x]);
}
if(dp[x]==m)
ans=max(ans,ret);
}
signed main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>coin[i];
sum+=coin[i];
}
for(int i=1;i<=m;i++)
{
cin>>val[i];
val[i]+=val[i-1];
}
for(int i=1;i<(1<<n);i++)
update(i);
cout<<ans;
}