【题解】[USACO12MAR]Cows in a Skyscraper G

题目链接

题目大意:给定一个集合\(S\),给一个限制条件\(P\),要求划分集合,使得每一个子集\(A\in S\),\(A\)满足限制条件\(P\),且划分总数最小。

注意到数据范围\(n<=18\).

第一感状压。

搜索不想写,于是\(dp\).

原本设计的状态是\(f[i][j]\)表示当前状态为\(i\),枚举到第\(j\)件物品的最大容量,\(g[i][j]\)表示状态为\(i\),枚举到\(j\)的最小划分数。然而太复杂了,没有必要,写崩了搞了\(16pts\).

换一种思路。设计\(f[i]\)表示状态为\(i\)的最大容量,\(g[i]\)表示状态为\(i\)的最小划分。显然,以\(f\)为第一关键字转移,若可以装,则优先转移该状态。遵循贪心策略,当前背包只要可以装,那就装。

注意的是,需要更新背包数量的时候,注意要回归到\(w-a[j]\)

代码:

#include<bits/stdc++.h>
using namespace std;
//设计g[i]表示状态为i的背包最大容量 
//f[i]表示状态为i的最小划分数
//
int n,w,ans=500;
int a[18]; 
int f[1<<18],g[1<<18];
int main(){
    scanf("%d%d",&n,&w);
    for(int i=0;i<n;++i)scanf("%d",&a[i]);
    sort(a,a+n);
    memset(f,0x3f,sizeof(f));
    g[0]=w;f[0]=1;
    for(int i=0;i<(1<<n);++i){
        for(int j=0;j<n;++j){
            if(i&(1<<j))continue;
            if(g[i]>=a[j]){
                if(f[i]<=f[i|(1<<j)]){
                    f[i|(1<<j)]=f[i];
                    g[i|(1<<j)]=max(g[i|(1<<j)],g[i]-a[j]);
                }
            }
            else{
                if(f[i]+1<=f[i|(1<<j)]){
                    f[i|(1<<j)]=f[i]+1;
                    g[i|(1<<j)]=max(g[i|(1<<j)],w-a[j]);
                }
            }
        }
    }   
    int T=(1<<n)-1;
    cout<<f[T]<<endl;
    return 0;
}

实现上可能有些许差异。注意两个数组代表的意义。

猜你喜欢

转载自www.cnblogs.com/h-lka/p/12422664.html
今日推荐