返回顶部

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;
    }
    
posted @ 2021-05-10 21:58  Rayotaku  阅读(527)  评论(0编辑  收藏  举报