• codeforces 1428G Lucky Numbers (贪心+dp)


    题目链接:https://codeforces.com/problemset/problem/1428/G1

    仔细分析发现,贪心地选取 0,3,6,9 组成的数字一定是最优的,
    而且这样的贪心策略,最多会有一个数字,不是全部由 0,3,6,9组成的

    考虑按位分组dp,每组都有体积为:3,6,9 * 位权,价值为的物品
    于是问题转变成了:先分组完全背包,每组物品中最多选 K 个,最后将每组合并,每组物品最多选 1 个
    但时间复杂度为(O(NMK)),直接起飞
    考虑优化:
    对于完全背包最多取 K 个的限制,考虑将 K 二进制分组,将每种物品拆成按 K 的二进制分组组合的物品,
    这样就转化成了 0/1 背包,复杂度为(O(NMlogK))

    对于简单版,询问只有一次,考虑只选(K-1)个物品,枚举那个特殊的数字 x ,统计答案
    还有个小性质:体积为6,9的物品都可以拆成为 3 的物品,就变成最多选 3*(k-1) 个物品了,必须这样优化,不然还是过不去

    将各组物品合并时:
    直接在每组内对该组进行背包更新答案,这样可以保证当前组物品不被重复选取

    未使用小性质优化的代码(TLE)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<stack>
    #include<queue>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 1000000;
    
    int k,m,num,cntf,cntg;
    int di[maxn];
    ll n,F[10],f[21][maxn],g[maxn],v[1000],w[1000],vg[1000],wg[1000];
    
    int qsm(int i,int po){
    	int res = 1;
    	while(po){
    		if(po&1) res *= i;
    		po >>= 1;
    		i *= i;
    	}
    	return res;
    }
    
    ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
    
    int main(){
    	num = 0;
    	k = read();
    	int K = k-1;
    	int t = 0;
    	while(K - (1<<t) > 0){ // 二进制分组 
    		di[++num] = 1<<t;
    		K -= (1<<t);
    		++t;
    	}
    	di[++num] = K;
    	for(register int i=0;i<6;++i) F[i] = read();
    	m = read();
    	
    	n = read();
    	
    	memset(f,-1,sizeof(f));
    	f[0][0] = 0;
    	
    	memset(g,0,sizeof(g));
    
    	for(register int d=1;d<=6;++d){ //  dp 
    		cntf = 0; cntg = 0;
    		for(int j=1;j<=num;++j){
    			v[++cntf] = 0, w[cntf] = 0;
    		}
    		
    		for(register int i=1;i<=3;++i){
    			ll wei = 1ll * (3*i) * qsm(10,d-1);
    			ll val = 1ll * i * F[d-1];
    			
    			for(register int j=1;j<=num;++j){
    				v[++cntf] = 1ll * di[j] * val;
    				w[cntf] = 1ll * di[j] * wei;
    			}
    		}
    		
    		if(d>1) for(int i=0;i<=n;++i) f[0][i] = f[num][i];
    		for(register int i=1;i<=cntf;++i){
    			for(register int p=1;p<=num;++p){
    				for(register int j=n;j>=w[i];--j){	
    					if(f[p-1][j-w[i]] != -1) f[p][j] = max(f[p][j],f[p-1][j-w[i]] + v[i]);
    				}
    			}
    		}
    	}
    	
        ll ans = 0;
    	for(register int x=0;x<=n;x++){
    		int tmp = x, o = 1;
    		ll ta = 0;
    		while(tmp > 0){
    			if((tmp%10) % 3 == 0){
    				ta += 1ll * (tmp % 10)/3 * F[o-1];
    			}
    			tmp /= 10;
    			++o;
    		}
    		ans = max(ans,f[num][n-x]+ ta);
    	}
    	
    	printf("%lld
    ",ans);
    	
    	return 0;
    }
    

    AC代码

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<stack>
    #include<queue>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 1000005;
    const ll inf = (1LL << 58LL);
    
    int k,m,num,cntf,cntg;
    int di[maxn];
    ll n,F[10],dp[maxn];
    
    int qsm(int i,int po){
    	int res = 1;
    	while(po){
    		if(po&1) res *= i;
    		po >>= 1;
    		i *= i;
    	}
    	return res;
    }
    
    ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
    
    int main(){
    	num = 0;
    	k = read();
    	int K = k-1;
    //	for(int i=1;i<=num;++i) printf("%d ",di[i]); printf("
    ");
    	
    	for(int i=0;i<6;++i) F[i] = read();
    	m = read();
    	
    	n = read();
    	fill(dp,dp+n+1,-1ll);
    	dp[0] = 0; 
    	for(int d=1;d<=6;++d){
    		ll left = 3 * (k-1);
    		ll group = 1;
    		while(left > 0){
    			group = min(group,left);
    			ll wei = 1ll * group * qsm(10,d-1) * 3 ;
    			ll val = 1ll *  group * F[d-1];
    			
    			for(int i=n; i>=wei; i--){
    				if(dp[i-wei]!=-1ll) dp[i] = max(dp[i],dp[i-wei] + val);
    			}
    			left -= group;
    			group *= 2;
    		}
    	}
    	
        ll ans = 0;
    	for(int x=0;x<=n;x++){
    		int tmp = x, o = 1;
    		ll ta = 0;
    		while(tmp > 0){
    			if((tmp%10) % 3 == 0){
    				ta += 1ll * (tmp % 10)/3 * F[o-1];
    			}
    			tmp /= 10;
    			++o;
    		}
    		if(dp[n-x]!=-1) ans = max(ans,dp[n-x]+ ta); //
    	}
    	
    	printf("%lld
    ",ans);
    	
    	return 0;
    }
    

    对于加强版,由于有多组询问,不可以再枚举 x,
    因为已经处理出前(K-1)个物品的 (DP) 表,按位考虑最后一个数字,更新 (DP)
    注意最后更新的时候要更换枚举顺序,wei维在外,物品维在内,这样可以保证每组物品最多只选一个

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<cmath>
    #include<stack>
    #include<queue>
    using namespace std;
    typedef long long ll;
    
    const int maxn = 1000010;
    const ll inf = (1ll << 58ll);
    
    int k,n,q;
    int ten[10] = {1,10,100,1000,10000,100000};
    ll F[7];
    ll dp[maxn],g[maxn];
    
    ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<'0' || ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0' && ch<='9'){ s=s*10+ch-'0'; ch=getchar(); } return s*f; }
    
    int main(){
    	k = read();
    	for(int i=0;i<6;++i) F[i] = read();
    	
    	fill(dp,dp+1+999999,-inf);
    //	for(int i=1;i<=10;++i) printf("%lld
    ",dp[i]);
    	dp[0] = 0;
    	
    	for(int d=1;d<=6;++d){
    		ll left = 1ll * 3 * (k-1);
    		ll group = 1ll;
    		
    		while(left > 0){ // 二进制分组 
    			group = min(group,left);
    			
    			ll val = 1ll * group * F[d-1];
    			ll wei = 1ll * 3 * group * ten[d-1];
    			
    			for(int i=999999;i>=wei;--i){
    				dp[i] = max(dp[i],dp[i-wei] + val);
    			}
    			left -= group;
    			group *= 2;
    		}
    		
    	}
    
    	for(int d=1;d<=6;++d){
    		for(int i=999999;i>=0;i--){
    			for(int j=0;j<=9;++j){
    				ll val, wei;
    				if(j==3 || j==6 || j==9) val = 1ll * (j/3) * F[d-1];
    				else val = 0;
    				wei = 1ll * j * ten[d-1];
    				
    				if(wei > i) break;
    				dp[i] = max(dp[i],dp[i-wei] + val);
    			}
    		}
    		
    	}	
    	
    	q = read();
    	
    	for(int i=1;i<=q;++i){
    		n = read();
    		printf("%lld
    ",dp[n]);
    	}
    	
    	return 0;
    }
  • 相关阅读:
    enyo学习之enyo.Object类
    java.lang.Object
    Nginx转发匹配规则
    工具类Redis工具类
    Redis从0到精通Redis.conf详解
    工具类类型转换器
    Docker入门到精通
    Redis从0到精通Jedis
    工具类字符集工具类
    Spock高质量单元测试实操篇
  • 原文地址:https://www.cnblogs.com/tuchen/p/13874026.html
Copyright © 2020-2023  润新知