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

相关内容

热门资讯

吃牙膏初一作文(实用6篇) 吃牙膏初一作文 篇一吃牙膏的危害牙膏是我们日常生活中必不可少的物品之一,它具有保护牙齿健康的作用。然...
我身边的胡胖作文800字(优... 我身边的胡胖作文800字 篇一胡胖是我的好朋友,他是一个胖胖的男孩,大家都亲切地叫他“胖胖”。胖胖的...
第一次吃自助餐初中作文(优选... 第一次吃自助餐初中作文 篇一第一次吃自助餐今天,我第一次去吃自助餐。这是我以前从未尝试过的新鲜事物,...
乐在元宵夜初中作文(精选6篇... 乐在元宵夜初中作文 篇一元宵节是我国传统的节日之一,也是中国农历正月十五的晚上。这一天,人们会举行各...
傣族泼水节作文(推荐6篇) 傣族泼水节作文 篇一傣族泼水节是中国云南省傣族人民传统的节日,也是中国国家级非物质文化遗产。每年农历...
以包汤圆为主题的优秀作文【优... 以包汤圆为主题的优秀作文 篇一包汤圆的乐趣包汤圆是中国传统的民间活动之一。每年农历正月十五的元宵节,...
校园生活二三事作文【最新6篇... 校园生活二三事作文 篇一我的校园生活充满了欢笑和感动。在这个充满活力的地方,我度过了许多难忘的时光。...
初一学生作文【精彩6篇】 初一学生作文 篇一:我的暑假计划初一学生作文 篇二:我的理想职业初一学生作文 篇三   我们平常看书...
自信作文【通用6篇】 自信作文 篇一自信是一种积极向上的心态,是人们在面对困难和挑战时保持坚定信念和积极态度的能力。自信是...
初中作文:爸爸,我想对您说【... 初中作文:爸爸,我想对您说 篇一亲爱的爸爸:您好!我想借此机会写一封信给您,表达我对您的感激之情和对...
初中生家长的寄语(优选5篇) 初中生家长的寄语 篇一亲爱的家长们:首先,我要向大家表示衷心的感谢,感谢你们一直以来对孩子的关心和支...
并列式议论文(实用5篇) 并列式议论文 篇一应该提高法定退休年龄随着现代医疗和生活水平的提高,人们的寿命也在不断延长。这使得许...
寂寞的天空初中作文【经典5篇... 寂寞的天空初中作文 篇一寂寞的天空天空,是一片广袤无垠的蓝色宇宙,是我们向往的自由之地。然而,有时候...
初中英语作文:美味的臭豆腐(... 初中英语作文:美味的臭豆腐 篇一Stinky Tofu: A Delicious DelicacyS...
亲情作文【优质6篇】 亲情作文 篇一:珍贵的亲情亲情,是我们生命中最珍贵的财富。无论是父母的爱护、兄弟姐妹的关怀,还是亲人...
成长路上的X初一作文20篇 成长路上的X初一作文 第一篇在一生当中,“人”没有完美的,只有尽量去做到完美!但如果怕去犯错或是做错...
雨的作文(精彩6篇) 雨的作文 篇一雨是大自然的一种神奇的馈赠,它给予了我们生活的滋润和希望。每当雨水纷纷扬扬地落在大地上...
春节的来历【最新5篇】 春节的来历 篇一:传统文化的庆典之始春节是中国传统文化中最重要、最热闹的节日之一。它的来历可以追溯到...
小城小爱作文800字【实用5... 小城小爱作文800字 篇一:感受小城的温暖小城,一个位于山水之间的宁静小地方,它虽然不大,但却给予我...
我眼中的春天300字初一作文... 我眼中的春天300字初一作文 篇一春天,是大自然的醒来,是万物复苏的季节。在我眼中,春天是一幅绚丽多...