[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;
}

相关内容

热门资讯

晚会结束语主持词 晚会结束语主持词范文汇总5篇  主持词要把握好吸引观众、导入主题、创设情境等环节以吸引观众。在当今社...
南安市延平中学第三届教代会第... 南安市延平中学第三届教代会第三次会议闭幕词范文各位代表、同志们:  历时一天的南安市延平中学第三届教...
《老友记》经典台词 《老友记》经典台词集锦  She just graduated. 她刚毕业。  We don't h...
新上岗同事接风宴会上的致辞 新上岗同事接风宴会上的致辞  谢谢大家,我想说三句话。  第一句:热烈欢迎。今天宴会的主题是:为新参...
迎新晚会主持词 迎新晚会主持词范文(精选9篇)  主持词要把握好吸引观众、导入主题、创设情境等环节以吸引观众。在人们...
读书节主持词 读书节主持词(精选15篇)  主持词要注意活动对象,针对活动对象写相应的主持词。现今社会在不断向前发...
宫崎骏《起风了》动漫的经典台... 宫崎骏《起风了》动漫的经典台词  台词(part/dialog/line)是戏剧表演中角色所说的话语...
婚礼回门答谢宴主持词 婚礼回门答谢宴主持词  尊敬的各位领导、各位来宾、朋友们,  大家中午好!  阳光明媚,歌声飞扬,欢...
重阳节精彩致辞 重阳节精彩致辞范文(通用7篇)  在平时的学习、工作或生活中,大家或多或少都用到过致辞吧,致辞受场合...
学校班级家长会主持词 学校班级家长会主持词  主持词要把握好吸引观众、导入主题、创设情境等环节以吸引观众。在如今这个时代,...
体操比赛主持词 关于体操比赛主持词4篇  主持词要根据活动对象的不同去设置不同的主持词。在如今这个中国,主持人在活动...
中学心理剧大赛主持词 中学心理剧大赛主持词  开幕  雪和华:亲爱的各位领导、各位老师、各位同学,大家晚上好!  雪:我是...
《网购奇遇》的经典台词 《网购奇遇》的经典台词  1、生命不息,扰民不止  2、你那个培训班办的,六年就一个学员还是你媳妇 ...
婚礼男方致辞 婚礼男方致辞婚礼男方致辞尊敬的各位来宾:大家好!今天是我与××小姐结婚的大好日子!我感到十分地高兴同...
结婚单位领导致辞 结婚单位领导致辞(通用18篇)  在平时的学习、工作或生活中,要用到致辞的地方还是很多的,致辞具有能...
《天若有情》里的经典台词 《天若有情》里的经典台词  1 如果他们只是单纯的男人和女人,他们能相爱吗?  2 偷东西 是罪恶的...
《黑执事》经典台词 《黑执事》经典台词  1、一旦拒绝了信仰,就不能再踏入神的大门。  2、那是必须的,,不管何时,王只...
风雨哈佛路经典台词 风雨哈佛路经典台词  引导语:《风雨哈佛路》这部影片相信很多人都看过,亦是一部非常好看的影片,那么有...
文艺节目主持词 文艺节目主持词四篇  主持词要把握好吸引观众、导入主题、创设情境等环节以吸引观众。在一步步向前发展的...
幼儿园六一儿童节主持词 幼儿园六一儿童节主持词尊敬的各位来宾、各位朋友大家下午好!沐浴着和风丽日,我们即将迎来花团锦簇、芳香...