2020-2021 ICPC Northwestern European Regional Programming Contest (NWERC 2020) A.Atomic Energy (贪心,dp)
-
题意:一个含有\(k\)个中子的原子,如果\(k\le n\),那么它就直接爆炸释放\(a_k\)大小的能量,否则它会分裂成含有\(i\)个和\(j\)个中子的原子(\(i+j=k\)),然后继续分裂.给你\(n\)和\(a_1,a_2,...,a_n\),询问\(q\)次,每次问你一个含有\(k\)个中子的原子分裂产生的最小能量.
-
题解:这题当时我开的时候,有一个很明显的贪心思路,就是可以先处理\(a\),找出对答案贡献最小的\(a_i\),然后对于\(k>n\)的原子,我们一定优先分裂\(i\)个最优,但是分裂到一定值的时候就不能继续这样分裂了,举个例子,假设\(n=4,k=8\),\(a_1=10000,a_2=1,a_3=4,a_4=10000\),那么首先将\(8\)分裂\(2\)和\(6\),很显然我们不能再将\(6\)分裂成\(2\)和\(4\),而应分成\(3\)和\(3\)最优.考虑到这,我想到可以类似完全背包来预处理前面一些状况,然后对于很大的\(k\)可以分成两段,一段用预处理的背包来给答案贡献,另一段就是\(cnt*a_i\),但是这个预处理的常数给我卡死了,不知道为啥开小了会wa,如果有大佬知道请评论告诉孩子QAQ,另外,这题可以不找对答案贡献最小的\(a_i\),\(n\)很小,完全可以枚举.
-
代码:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 5e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} int n,q; ll a[N]; ll dp[N]; int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>n>>q; rep(i,1,n) cin>>a[i]; rep(i,1,5000000) dp[i]=5e18; rep(i,1,n) dp[i]=a[i]; rep(j,n+1,5000000){ rep(i,1,n){ dp[j]=min(dp[j],dp[j-i]+dp[i]); } } while(q--){ ll ans=5e18; ll k; cin>>k; if(k<=1000000){ cout<<dp[k]<<'\n'; continue; } k-=1000000; rep(i,1,n){ ll cnt=k/i; ll rest=k%i; ans=min(ans,cnt*a[i]+dp[1000000+rest]); } cout<<ans<<'\n'; } return 0; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮