贪心+DP动态规划 题解 100% AC
2025-08-23 11:52:17
发布于:浙江
131阅读
0回复
0点赞
含无注释代码:
1. 贪心解法:
#include <bits/stdc++.h>
using namespace std;
/*
* 解题思路:
* 1. 数字拼法规则:每个数字需要特定数量的小木棍(0:6根,1:2根,2:5根,3:5根,4:4根,5:5根,6:6根,7:3根,8:7根,9:6根):
* 2. 核心策略:优先用最少木棍拼出最小数字,特殊处理余数情况:
* 3. 贪心原则:在满足条件的情况下,数字位数越少越好,同位数时字典序越小越好:
*/
int main(){
long long T; // 测试数据组数
cin >> T;
while(T--){
long long n;
cin >> n; // 当前组木棍数量
// 特殊情况处理(1-7根木棍的基准情况)
if(n == 1){
// 1根无法拼出任何数字(最小数字1需要2根)
cout << -1;
} else if(n == 2){
// 2根只能拼出数字1
cout << 1;
} else if(n == 3){
// 3根拼出7(最优解)
cout << 7;
} else if(n == 4){
// 4根拼出4(比11更优)
cout << 4;
} else if(n == 5){
// 5根拼出2(比71更优)
cout << 2;
} else if(n == 6){
// 6根拼出6(比111更优)
cout << 6;
} else if(n == 7){
// 7根拼出8(最优解)
cout << 8;
} else if(n == 10){
// 特殊处理10根情况(拼出22比118更优):
cout << 22;
}
// 通用情况处理(n≥8)
else if(n % 7 == 0){
// 能被7整除时全用8(7根/个)是最优解:
for(int i = 1; i <= n/7; i++) cout << 8;
} else if(n % 7 == 1){
/* 余1时:用10开头(8根) + (n-8)/7个8
* 例如15根:10 + 8 = 108(比228更优):
*/
cout << 10;
for(int i = 1; i <= (n-8)/7; i++) cout << 8;
} else if(n % 7 == 2){
// 余2时:1开头 + (n-2)/7个8
cout << 1;
for(int i = 1; i <= (n-2)/7; i++) cout << 8;
} else if(n % 7 == 3){
/* 余3时:分情况处理:
* - 当n≥17时用200开头(17根) + (n-17)/7个8
* - 否则直接用7(如n=3):
*/
if(n == 10) cout << 22; // 特殊处理
else {
cout << 200;
for(int i = 1; i <= (n-17)/7; i++) cout << 8;
}
} else if(n % 7 == 4){
// 余4时:20开头(11根) + (n-11)/7个8
cout << 20;
for(int i = 1; i <= (n-11)/7; i++) cout << 8;
} else if(n % 7 == 5){
// 余5时:2开头(5根) + (n-5)/7个8
cout << 2;
for(int i = 1; i <= (n-5)/7; i++) cout << 8;
} else if(n % 7 == 6){
// 余6时:6开头(6根) + (n-6)/7个8
cout << 6;
for(int i = 1; i <= (n-6)/7; i++) cout << 8;
}
cout << endl; // 每组数据换行
}
return 0;
}
关键点说明:
1. 数字消耗表:每个数字需要的小木棍数量是解题基础(如8需要7根,1需要2根等)
2. 贪心策略:在满足条件的情况下,优先保证:
- 数字位数最少(多用消耗木棍多的数字8)
- 同位数时字典序最小(如选20而不选11)
3.特殊余数处理:
- 余1时用"10"组合(避免出现前导0)
- 余3时用"200"组合(比多个小数字更优)
- 其他余数选择最小前缀数字
复杂度分析:
- 时间复杂度:,每组数据最多循环次
- 空间复杂度:,仅用常数空间存储变量
无注释代码:
#include <bits/stdc++.h>
using namespace std;
int main(){
long long T;
cin>>T;
while(T--){
long long n;
cin>>n;
if(n==1)cout<<-1;
else if(n==2)cout<<1;
else if(n==3)cout<<7;
else if(n==4)cout<<4;
else if(n==5)cout<<2;
else if(n==6)cout<<6;
else if(n==7)cout<<8;
else if(n==10)cout<<22;
else if(n%7==0)for(int i=1;i<=n/7;i++)cout<<8;
else if(n%7==1){
cout<<10;
for(int i=1;i<=(n-8)/7;i++)cout<<8;
}else if(n%7==2){
cout<<1;
for(int i=1;i<=(n-2)/7;i++)cout<<8;
}else if(n%7==3){
cout<<200;
for(int i=1;i<=(n-17)/7;i++)cout<<8;
}else if(n%7==4){
cout<<20;
for(int i=1;i<=(n-11)/7;i++)cout<<8;
}else if(n%7==5){
cout<<2;
for(int i=1;i<=(n-5)/7;i++)cout<<8;
}else if(n%7==6){
cout<<6;
for(int i=1;i<=(n-6)/7;i++)cout<<8;
}
cout<<endl;
}
return 0;
}
简化版:
#include<bits/stdc++.h>
using namespace std;
long long T,n;
string a1[]={"-1","-1","1","7","4","2","6","8"},a2[]={"8","10","1","200","20","2","6"};long long a3[]={0,1,0,2,1,0,0};
int main(){
cin>>T;
while(T--){
cin>>n;
if(n==1)cout<<"-1";
else if(n==10)cout<<"22";
else if(n<=7)cout<<a1[n];
else{
if(n%7==0)for(long long i=1;i<=n/7;i++)cout<<"8";
else{
cout<<a2[n%7];
long long a=n/7-a3[n%7];
for(long long i=1;i<=a;i++)cout<<"8";
}
}
cout<<endl;
}
return 0;
}
2. DP解法:
- 数据结构设计:
- 使用结构体node存储数字的组成情况,其中a[7]数组记录不同数字的个数
- dp数组存储每个火柴棒数量对应的最优解
- 核心算法实现:
#include <iostream>
#include <cstdio>
using namespace std;
// 定义结构体存储数字组成情况
struct node{
int a[7]; // 用桶记录数字0-6的出现次数
}dp[100005]; // dp数组,存储每个n对应的最优解
// 数字转换表:0-6对应实际数字0,1,2,4,6,7,8
const int turn[7] = {0, 1, 2, 4, 6, 7, 8};
// 每个数字(0-9)需要的火柴棒数量
const int number[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
// 获取数字的首位(不能为0)
int get_head(node &n){
for(int i = 1; i <= 6; i++){ // 从1开始找,避免前导0
if(n.a[i]){
n.a[i]--;
return turn[i]; // 返回对应的实际数字
}
}
}
// 将数字组合转换为字符串
string get(node n){
string s;
s = get_head(n) + '0'; // 获取首位
for(int i = 0; i <= 6; i++){ // 拼接剩余数字
while(n.a[i]){
s += turn[i] + '0';
n.a[i]--;
}
}
return s;
}
// 比较两个数字组合哪个更优
bool cmp(node a, node b){
// 比较数字位数
int lena = 0, lenb = 0;
for(int i = 0; i <= 6; i++){
lena += a.a[i], lenb += b.a[i];
}
if(lena < lenb) return 1; // 位数少的更优
if(lena > lenb) return 0;
// 位数相同比较首位
int tmp1 = get_head(a), tmp2 = get_head(b);
if(tmp1 < tmp2) return 1;
if(tmp1 > tmp2) return 0;
// 首位相同比较剩余数字
for(int i = 0; i <= 6; i++){
if(a.a[i] < b.a[i]) return 0;
if(a.a[i] > b.a[i]) return 1;
}
return 0;
}
int main(){
// 初始化基础情况(2-7根火柴棒)
dp[2].a[1] = 1; // 2根→数字1
dp[3].a[5] = 1; // 3根→数字7
dp[4].a[3] = 1; // 4根→数字4
dp[5].a[2] = 1; // 5根→数字2
dp[6].a[4] = 1; // 6根→数字6
dp[7].a[6] = 1; // 7根→数字8
// 动态规划填充dp数组(8-100000)
for(int i = 8; i <= 100000; i++){
for(int j = 0; j <= 6; j++){ // 尝试添加每个数字
if(i - number[turn[j]] <= 1) continue; // 跳过无效情况
node n = dp[i - number[turn[j]]]; // 转移状态
n.a[j]++; // 添加当前数字
if(cmp(n, dp[i])) dp[i] = n; // 更新最优解
}
}
// 处理输入输出
int t;
cin >> t;
while(t--){
int n;
cin >> n;
// 1根火柴棒无解,其他情况输出结果
cout << (n == 1 ? "-1\n" : get(dp[n]) + '\n');
}
return 0;
}
- 算法思路详解:
- 该问题要求用恰好n根火柴棒拼出最小的正整数
- 动态规划状态转移方程:
- 优先使用最少火柴棒的数字组合,确保数字尽可能小
- 特殊处理前导0问题,首位不能为0
- 关键优化点:
- 使用桶排序思想记录数字出现次数,提高比较效率
- 预处理2-7根火柴棒的基础情况
- 动态规划预处理所有可能情况,查询时只需时间
- 复杂度分析:
- 预处理时间复杂度:
- 查询时间复杂度:
- 空间复杂度:
- 该算法通过动态规划预处理所有可能情况,使得查询时只需O(1)时间即可得到结果,适合处理大量查询请求
该算法通过动态规划预处理所有可能情况,使得查询时只需O(1)时间即可得到结果,适合处理大量查询请求代码结构清晰,使用了常见的动态规划优化技巧,能够高效解决该问题
无注释代码:
#include <bits/stdc++.h>
using namespace std;
struct node{
int a[7];
}dp[100005];
const int turn[7] = {0, 1, 2, 4, 6, 7, 8}, number[10] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6};
int get_head(node &n){
for(int i = 1; i <= 6; i++){
if(n.a[i]){
n.a[i]--;
return turn[i];
}
}
}
string get(node n){
string s;
s = get_head(n) + '0';
for(int i = 0; i <= 6; i++){
while(n.a[i]){
s += turn[i] + '0';
n.a[i]--;
}
}
return s;
}
bool cmp(node a, node b){
int lena = 0, lenb = 0;
for(int i = 0; i <= 6; i++){
lena += a.a[i], lenb += b.a[i];
}
if(lena == 0) return 0;
if(lenb == 0) return 1;
if(lena < lenb) return 1;
if(lena > lenb) return 0;
int tmp1 = get_head(a), tmp2 = get_head(b);
if(tmp1 < tmp2) return 1;
if(tmp1 > tmp2) return 0;
for(int i = 0; i <= 6; i++){
if(a.a[i] < b.a[i]) return 0;
if(a.a[i] > b.a[i]) return 1;
}
return 0;
}
int main(){
dp[2].a[1] = 1, dp[3].a[5] = 1, dp[4].a[3] = 1, dp[5].a[2] = 1, dp[6].a[4] = 1, dp[7].a[6] = 1;
for(int i = 8; i <= 100000; i++){
for(int j = 0; j <= 6; j++){
if(i - number[turn[j]] <= 1) continue;
node n = dp[i - number[turn[j]]];
n.a[j]++;
if(cmp(n, dp[i])) dp[i] = n;
}
}
int t;
cin >> t;
while(t--){
int n;
cin >> n;
cout << (n == 1 ? "-1\n" : get(dp[n]) + '\n');
}
return 0;
}
全部评论 2
%%% DP还在蒸
2025-08-25 来自 辽宁
1顶
2025-08-19 来自 浙江
0顶
2025-08-21 来自 江苏
1
有帮助,赞一个