bzoj3717 [PA2014]Pakowanie (状压dp)

qwq貌似看起来很简单的一个题…

但是貌似复杂度写的有点假。

我们令 f [ s ] f[s] 表示已经装进了集合 s s 中的物品的最少花费的背包数量,然后 g [ s ] g[s] 则表示最后一个背包最多剩下多少的体积。

qwq但是我们发现,由于我们不能够状压背包,那我们就需要考虑一下该怎么处理背包这个问题。

一个比较显然的想法,我们优先使用体积大的背包。因为我们是状压dp,所以这个顺序是对的,不会出现那种因为放了小的物品而导致大的物品放不进去的情况,总会枚举到正确的情况的。

需要注意的是,物品不能拆分,如果我们对于当前位置,剩下的背包都不能够放进,就直接 c o n t i n u e continue

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 4e7+1e2;
const int maxm = 1e5+1e2;
int f[maxn],g[maxn];
int a[maxm],c[maxm];
int n,m;
bool cmp(int a,int b)
{
	return a>b;
}
int main()
{
  n=read(),m=read();
  for (int i=1;i<=n;i++) a[i]=read();
  for (int i=1;i<=m;i++) c[i]=read();
  sort(c+1,c+1+m,cmp);
  memset(f,127/3,sizeof(f));
  f[0]=0;
  g[0]=0;
  for (register int i=0;i<(1<<n);++i)
  {
  	  if (f[i]==f[maxn-3]) continue;
  	  for (register int j=1;j<=n;j++)
  	  {
  	  	if ((1<<(j-1))^i)
  	  	{
  	  		int now = a[j];
  	  		int x = g[i];
  	  		int y = f[i];
  	  		if(x>=a[j])
  	  		{
  	  			x=x-a[j];
			}
			else
			{
				y++;
				while (a[j]>c[y] && y<=m)
				{
					y++;
				}
				if (y==m+1)
				{
					continue;
				 } 
                x=c[y]-a[j];
			}
			int tmp = (i|(1<<(j-1)));
			if(f[tmp]>y) f[tmp]=y,g[tmp]=x;
			else if (f[tmp]==y && g[tmp]<x) g[tmp]=x;  
		}
	  }
  }
  if (f[(1<<n)-1]==f[maxn-3]) cout<<"NIE\n";
  else cout<<f[(1<<n)-1]<<endl; 
  return 0;
}

猜你喜欢

转载自blog.csdn.net/y752742355/article/details/87868216
今日推荐