P3985 不开心的金明(STL 灵活应用,Meet in the Middle)

所谓 STL 灵活应用是下面这个思路。
想法是用 map 直接暴力转移,
至于时间复杂度就不得而知了(

#include<bits/stdc++.h>
using namespace std;
int n,V,ans;
map<int,int> mp,mp2;
struct node{int v,w;} a[109];
int main(){
    scanf("%d%d",&n,&V),a[0].w=-1,mp[0]=0;
    for(int i=1;i<=n;i++) scanf("%d%d",&a[i].w,&a[i].v);
    for(int j=1;j<=n;j++){
        //利用辅助数组,防止一边便利一边存储导致 STL 出奇奇怪怪的 bug。
        for(auto k:mp) if(k.first+a[j].w<=V)
            mp2[k.first+a[j].w]=max(mp[k.first+a[j].w],k.second+a[j].v);
        for(auto k:mp2)
            mp[k.first]=k.second;
        mp2.clear();
    }
    //注意答案未必像正经 01 背包一样存储在 mp[V] 或者最大值 mp.rbegin()-second 里
    for(auto k:mp) ans=max(ans,k.second);
    printf("%d\n",ans);
    return 0;
}

所以过了
好了抽象到此结束
其实相当于物体只有四类,枚举各个物体取多少个,再用贪心即可。
理论时间复杂度 \(O(n^4)\)

#include<bits/stdc++.h>
using namespace std;
const int N=109;
int n,V,ans,w[N],v[N],siz[4];
vector<int> A[4];
int main(){
    int tmp=INT_MAX;
    scanf("%d%d",&n,&V);
    for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&v[i]),tmp=min(tmp,w[i]);
    for(int i=0;i<4;i++) A[i].push_back(0);
    for(int i=1;i<=n;i++) A[w[i]-tmp].push_back(v[i]);
    for(int i=0;i<4;i++){
        sort(A[i].begin()+1,A[i].end(),greater<int>()),siz[i]=A[i].size()-1;
        for(int j=1;j<=siz[i];j++) A[i][j]+=A[i][j-1];
    }
    for(int a=0;a<=siz[0]&&a*tmp<=V;a++)
        for(int b=0;b<=siz[1]&&a*tmp+b*(tmp+1)<=V;b++)
            for(int c=0;c<=siz[2]&&a*tmp+b*(tmp+1)+c*(tmp+2)<=V;c++)
                ans=max(ans,A[0][a]+A[1][b]+A[2][c]+A[3][min(siz[3],(V-(a*tmp+b*(tmp+1)+c*(tmp+2)))/(tmp+3))]);
    printf("%d\n",ans);
    return 0;
}

szj(%):遇事不决,Meet in the Middle!

两组两组 \(O(n^2)\) 合并,得到平方级别的 \((w_i,v_i)\)
排序,使得 \(w_i,v_i\) 均单调递增,然后枚举其中一组采用二分方式找出对应 \(v_i\),时间复杂度 \(O(n^2\log^2n)\)

#include<bits/stdc++.h>
using namespace std;
const int N=109;
int n,V,ans,w[N],v[N],siz[4];
vector<int> A[4];
struct node{int w,v,nxt=0;} L1[N*N],L2[N*N];
bool cmp(node a,node b){
    return (a.w==b.w)?(a.v>b.v):(a.w<b.w);
}
int main(){
    int tmp=INT_MAX;
    scanf("%d%d",&n,&V);
    for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&v[i]),tmp=min(tmp,w[i]);
    for(int i=0;i<4;i++) A[i].push_back(0);
    for(int i=1;i<=n;i++) A[w[i]-tmp].push_back(v[i]);
    for(int i=0;i<4;i++){
        sort(A[i].begin()+1,A[i].end(),greater<int>()),siz[i]=A[i].size()-1;
        for(int j=1;j<=siz[i];j++) A[i][j]+=A[i][j-1];
    }
    int cnt1=0,cnt2=0;
    for(int i=0;i<=siz[0]&&i*tmp<=V;i++) for(int j=0;j<=siz[1]&&i*tmp+j*(tmp+1)<=V;j++)
        L1[++cnt1]={i*tmp+j*(tmp+1),A[0][i]+A[1][j],0};//,printf("%d %d\n",i,j);
    for(int i=0;i<=siz[2]&&i*(tmp+2)<=V;i++) for(int j=0;j<=siz[3]&&i*(tmp+2)+j*(tmp+3)<=V;j++)
        L2[++cnt2]={i*(tmp+2)+j*(tmp+3),A[2][i]+A[3][j],0};
    sort(L1+1,L1+cnt1+1,cmp),sort(L2+1,L2+cnt2+1,cmp);
    for(int i=1,lst=-1;i<=cnt2;i++)
        if(L2[i].v<=lst) L2[i].nxt=1;
        else lst=L2[i].v;
    for(int i=1,lst=-1,np=cnt2;np>0&&i<=cnt1;i++){
        if(L1[i].v<=lst) continue; else lst=L1[i].v;
        while(L2[np].nxt==1||L1[i].w+L2[np].w>V) --np;
        ans=max(ans,L1[i].v+L2[np].v);
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2026-02-13 16:58  2025ing  阅读(0)  评论(0)    收藏  举报