[THUPC 2023 初赛] 背包
创始人
2024-05-31 22:18:19
0

[THUPC 2023 初赛] 背包

题目大意

有nnn种物品,第iii种物品体积为viv_ivi​,价值为cic_ici​。

有qqq次询问,每次给出背包的体积VVV,你可以选择若干种物品,每种物品可以选择任意多个(也可以不选),在选出物品的体积的和恰好为VVV的前提下最大化选出物品的价值的和。输出最大的价值和。如果不存在体积恰好为VVV的方案,输出−1-1−1。

VVV远大于viv_ivi​。


题解

第一步:DP

因为VVV远大于viv_ivi​,显然我们可以先放性价比最高的物品知道无法再放为止,再用性价比相对较低的物品来替换其中的一部分使得最后的体积恰好为VVV。一个物品的性价比,即civi\dfrac{c_i}{v_i}vi​ci​​。

设性价比最高的物品为kkk,我们假设这个书包放物品kkk的数量可以为实数,那么最大的价值和为ckvk×V\dfrac{c_k}{v_k}\times Vvk​ck​​×V(注意小数不能舍去)。当然,这只是假设,我们还需要用其他物品来替换。

设fjf_jfj​中的jjj表示当前选择了若干个非kkk的物品,这些物品的体积和对vkv_kvk​取模为jjj,设这些物品的体积和为ppp,价值和为qqq,则fjf_jfj​为p×ckvk−qp\times \dfrac{c_k}{v_k}-qp×vk​ck​​−q的最小值。fjf_jfj​的意义就是替换使得所有物品模vkv_kvk​为jjj后物品的总价值相对于原来放满物品kkk,其价值减少了多少。

初始值f0=0f_0=0f0​=0,其余的fff值设为极大值,转移式为

f(j+vi)%vk=min⁡(f(j+vi)%vk,fj+ci−ckvk×vi)f_{(j+v_i)\% v_k}=\min(f_{(j+v_i)\% v_k},f_j+c_i-\dfrac{c_k}{v_k}\times v_i)f(j+vi​)%vk​​=min(f(j+vi​)%vk​​,fj​+ci​−vk​ck​​×vi​)

若背包体积为ttt,如果ft%vkf_{t\%v_k}ft%vk​​的值为初始值,则输出−1-1−1;否则答案为ckvk×t−ft%vk\dfrac{c_k}{v_k}\times t-f_{t\% v_k}vk​ck​​×t−ft%vk​​,可以证明这一定是一个整数。为了方便,在计算中可以先将所有数都乘上vkv_kvk​,在最后除回来即可,不会爆long long。

不过,我们发现,如果这样做的话,你要枚举每种物品,再枚举每种状态,还要枚举这种物品放的个数,时间复杂度为O(n⋅vk2)O(n\cdot v_k^2)O(n⋅vk2​),这样做的话会超时。

DP是要用的,不过我们可以换一种方法转移。


第二步:dijkstra

上面说到的状态转移式

f(j+vi)%vk=min⁡(f(j+vi)%vk,fj+ci−ckvk×vi)f_{(j+v_i)\% v_k}=\min(f_{(j+v_i)\% v_k},f_j+c_i-\dfrac{c_k}{v_k}\times v_i)f(j+vi​)%vk​​=min(f(j+vi​)%vk​​,fj​+ci​−vk​ck​​×vi​)

我们可以让点jjj向点((j+vi)%k)((j+v_i)\% k)((j+vi​)%k)连一条边权为ci−ckvk×vic_i-\dfrac{c_k}{v_k}\times v_ici​−vk​ck​​×vi​的边,然后在这个图上跑dijkstra即可求出所有的fff值。时间复杂度为O(n⋅vk+vklog⁡vk)O(n\cdot v_k+v_k\log v_k)O(n⋅vk​+vk​logvk​)。

code

#include
using namespace std;
int n,tq,tot=0,z[1000005],d[5000005],l[5000005],r[5000005];
long long k,ans,v[5000005],f[1000005];
struct node{long long v,c;
}w[105];
queueq;
bool cmp(node ax,node bx){return ax.c*bx.v>bx.c*ax.v;
}
long long in(){long long t=0;char ch=getchar();while(ch<'0'||ch>'9') ch=getchar();while(ch>='0'&&ch<='9'){t=t*10+ch-'0';ch=getchar();}return t;
}
void add(int xx,int yy,long long zz){l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;v[tot]=zz;
}
int main()
{n=in();tq=in();for(int i=1;i<=n;i++){w[i].v=in();w[i].c=in();}sort(w+1,w+n+1,cmp);for(int i=2;i<=n;i++){for(int j=0;jadd(j,(j+w[i].v)%w[1].v,w[1].c*w[i].v-w[i].c*w[1].v);}}for(int i=1;iint u=q.front();q.pop();z[u]=0;for(int i=r[u];i;i=l[i]){if(f[d[i]]>f[u]+v[i]){f[d[i]]=min(f[d[i]],f[u]+v[i]);if(!z[d[i]]){q.push(d[i]);z[d[i]]=1;}}}}while(tq--){k=in();if(f[k%w[1].v]==2e18){printf("-1\n");continue;}ans=(k*w[1].c-f[k%w[1].v])/w[1].v;if(ans<0) ans=-1;printf("%lld\n",ans);}return 0;
}

第三步:set

打完上面的代码,就能AC了吗?

不能,还是会TLE。

因为在用队列的时候,每个元素可能会被加入优先队列多次,这样进出队列的次数会很大,时间会增大。

所以我们不能用优先队列,但可以用set。把所有优先队列的操作改为set的操作,并保持set中没有相同的元素即可。

时间复杂度为O(n⋅vk+vklog⁡vk)O(n\cdot v_k+v_k\log v_k)O(n⋅vk​+vk​logvk​),虽然set的常数比较大,但不会出现上面优先队列的情况。

下面是AC代码。

code

#include
using namespace std;
int n,tq,tot=0,z[1000005],d[10000005],l[10000005],r[10000005];
long long k,ans,v[10000005],f[1000005];
struct node{long long v,c;
}w[105];
struct vt{int x;long long dis;bool operator<(const vt ax)const{if(dis!=ax.dis) return diss;
bool cmp(node ax,node bx){return ax.c*bx.v>bx.c*ax.v;
}
long long in(){long long t=0;char ch=getchar();while(ch<'0'||ch>'9') ch=getchar();while(ch>='0'&&ch<='9'){t=t*10+ch-'0';ch=getchar();}return t;
}
void add(int xx,int yy,long long zz){l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;v[tot]=zz;
}
int main()
{n=in();tq=in();for(int i=1;i<=n;i++){w[i].v=in();w[i].c=in();}sort(w+1,w+n+1,cmp);for(int i=2;i<=n;i++){for(int j=0;jadd(j,(j+w[i].v)%w[1].v,w[1].c*w[i].v-w[i].c*w[1].v);}}for(int i=1;i0,0});s.insert((vt){1e9,2e18});while(s.size()>1){int u=(*s.begin()).x;s.erase(s.begin());if(z[u]) continue;z[u]=1;for(int i=r[u];i;i=l[i]){if(f[u]+v[i]set::iterator it=s.lower_bound((vt){d[i],f[d[i]]});if((*it).x==d[i]) s.erase(it);f[d[i]]=f[u]+v[i];s.insert((vt){d[i],f[d[i]]});}}}while(tq--){k=in();if(f[k%w[1].v]==2e18){printf("-1\n");continue;}ans=(k*w[1].c-f[k%w[1].v])/w[1].v;if(ans<0) ans=-1;printf("%lld\n",ans);}return 0;
}

相关内容

热门资讯

常用商务英语口语   商务英语是以适应职场生活的语言要求为目的,内容涉及到商务活动的方方面面。下面是小编收集的常用商务...
六年级上册英语第一单元练习题   一、根据要求写单词。  1.dry(反义词)__________________  2.writ...
复活节英文怎么说 复活节英文怎么说?复活节的英语翻译是什么?复活节:Easter;"Easter,anniversar...
2008年北京奥运会主题曲 2008年北京奥运会(第29届夏季奥林匹克运动会),2008年8月8日到2008年8月24日在中华人...
英语道歉信 英语道歉信15篇  在日常生活中,道歉信的使用频率越来越高,通过道歉信,我们可以更好地解释事情发生的...
六年级英语专题训练(连词成句... 六年级英语专题训练(连词成句30题)  1. have,playhouse,many,I,toy,i...
上班迟到情况说明英语   每个人都或多或少的迟到过那么几次,因为各种原因,可能生病,可能因为交通堵车,可能是因为天气冷,有...
小学英语教学论文 小学英语教学论文范文  引导语:英语教育一直都是每个家长所器重的,那么有关小学英语教学论文要怎么写呢...
英语口语学习必看的方法技巧 英语口语学习必看的方法技巧如何才能说流利的英语? 说外语时,我们主要应做到四件事:理解、回答、提问、...
四级英语作文选:Birth ... 四级英语作文范文选:Birth controlSince the Chinese Governmen...
金融专业英语面试自我介绍 金融专业英语面试自我介绍3篇  金融专业的学生面试时,面试官要求用英语做自我介绍该怎么说。下面是小编...
我的李老师走了四年级英语日记... 我的李老师走了四年级英语日记带翻译  我上了五个学期的小学却换了六任老师,李老师是带我们班最长的语文...
小学三年级英语日记带翻译捡玉... 小学三年级英语日记带翻译捡玉米  今天,我和妈妈去外婆家,外婆家有刚剥的`玉米棒上带有玉米籽,好大的...
七年级英语优秀教学设计 七年级英语优秀教学设计  作为一位兢兢业业的人民教师,常常要写一份优秀的教学设计,教学设计是把教学原...
我的英语老师作文 我的英语老师作文(通用21篇)  在日常生活或是工作学习中,大家都有写作文的经历,对作文很是熟悉吧,...
英语老师教学经验总结 英语老师教学经验总结(通用19篇)  总结是指社会团体、企业单位和个人对某一阶段的学习、工作或其完成...
初一英语暑假作业答案 初一英语暑假作业答案  英语练习一(基础训练)第一题1.D2.H3.E4.F5.I6.A7.J8.C...
大学生的英语演讲稿 大学生的英语演讲稿范文(精选10篇)  使用正确的写作思路书写演讲稿会更加事半功倍。在现实社会中,越...
VOA美国之音英语学习网址 VOA美国之音英语学习推荐网址 美国之音网站已经成为语言学习最重要的资源站点,在互联网上还有若干网站...
商务英语期末试卷 Part I Term Translation (20%)Section A: Translate ...