记录路径dp-4713-Permutation

这是一篇关于HDU 4713题目的解题报告,题目要求找到不超过给定数的能分成的最大的最小公倍数的对数,并输出路径。解题者采用了动态规划(DP)的方法,由于n值较大,存储路径时避免使用二维数组,而是记录每个状态的所有到达路径。关键在于输出路径时,确保较小的数值在前,并形成环状输出。文中包含解题思路及代码实现。

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=4713

题目大意:

题意同HDU 3092这不过这题要输出路径。

解题思路:

思路同HDU 3092

因为n比较大,不能开二维只记录前面一个来 逆着存路径。

所以对于每个状态,把到达它的所有数都保存下来。转移的时候将前面的路径也赋值过来。

dp[i]表示表示不超过i的能分成的最大的最小公倍数的对数。少了的话用1来凑。

注意:输出的时候值小的在前面,+1成环输出。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF 0x3fffffff
#define PI acos(-1.0)
#define ll __int64
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;


#define Maxn 10000 //三千内的质数430个

double dp[Maxn+10]; //取对数保证最小公倍数不会溢出
//dp[i]表示i能分成的最大的最小公倍数的对数
bool tmp[Maxn+10];
int pp[Maxn+10],ans[Maxn+10];
vector<int>myv[Maxn+10];

int n,cnt;

void init()
{
    cnt=0;
    memset(tmp,false,sizeof(tmp));
    for(int i=2;i<=Maxn;i++) //素数晒选法
    {
        if(!tmp[i])
        {
            pp[++cnt]=i;
            for(int j=i*2;j<=Maxn;j+=i)
                tmp[j]=true;
        }
    }
    return ;
}

void solve()
{
    memset(dp,0,sizeof(dp));
    for(int i=0;i<=n;i++)
        myv[i].clear();
    for(int i=1;i<=cnt&&pp[i]<=n;i++)
    {
        double tt=log10(pp[i]*1.0);
        for(int j=n;j>=pp[i];j--) //相同质数应做为一个整体考虑
        {
            for(int k=pp[i],num=1;k<=j;k=k*pp[i],num++)
                if(dp[j-k]+tt*num>dp[j])
                {
                    dp[j]=dp[j-k]+tt*num;
                    myv[j]=myv[j-k];
                    myv[j].push_back(k);
                }
        }
    }
}

int main()
{
    init();
    //printf("%d\n",cnt);
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        solve();
        int t=myv[n].size();
        int sum=0;

        for(int i=0;i<t;i++)
            sum+=myv[n][i];
        sum=n-sum;
        while(sum--)
            myv[n].push_back(1);
       sort(myv[n].begin(),myv[n].end());
        int s=0;
        for(int i=0;i<myv[n].size();i++)
        {
            for(int j=1;j<myv[n][i];j++)
                printf("%d ",s+j+1);
            printf("%d",s+1);
            if(i!=myv[n].size()-1)
                putchar(' ');
            s+=myv[n][i];
        }
        putchar('\n');
    }

    return 0;
}


#include <iostream> #include <vector> #include <string> #include <cstdlib> #include <ctime> #include <algorithm> #include <limits> #include "common.h" using namespace std; // 函数声明 void show_intro(); void press_enter(); int choose_level(); bool play_level1(); bool play_level2(); bool play_level3(); int main() { srand(time(0)); // 全局种子 while (true) { show_intro(); int current_level = 1; bool game_over = false; while (!game_over && current_level <= 3) { int choice; cout << "第一关:寻找地图(按键1)\n"; cout << "第二关:集齐碎片(按键2)\n"; cout << "第三关:合成魔杖(按键3)\n"; cout << "请输入要挑战的关卡:\n"; cin >> choice; if (choice == 1 && current_level == 1) { if (play_level1()) { cout << "\n--- 恭喜你找到了藏有地图的魔法书!\n"; cout << "--- 成功进入第二关。\n\n"; current_level = 2; } else { game_over = true; } } else if (choice == 2 && current_level >= 2) { if (play_level2()) { cout << "\n--- 碎片已全部收集完毕!\n"; cout << "--- 成功进入第三关。\n\n"; current_level = 3; } else { game_over = true; } } else if (choice == 3 && current_level >= 3) { if (play_level3()) { cout << "\n--- 魔杖成功合成!光芒照亮了整个城堡!\n"; cout << "--- 你拯救了魔法世界!\n\n"; cout << "--- 游戏通关!感谢游玩!\n"; return 0; } else { game_over = true; } } else { cout << "\n--- 当前无法进入该关卡,请按顺序挑战。\n\n"; } if (game_over) { cout << "\n--- 很遗憾,挑战失败。\n"; cout << "--- 游戏即将重新开始...\n"; press_enter(); } } } return 0; } void show_intro() { system("cls"); cout << "========================================\n"; cout << " 欢迎进入《迷失的魔杖》冒险游戏 \n"; cout << "========================================\n\n"; cout << "姓名:陈柔\n"; cout << "学号:2520636143\n\n"; cout << "故事背景:\n"; cout << "很久以前,一把强大的魔杖在战斗中碎裂成五块,\n"; cout << "地图被藏进魔法学院的一本书中。\n"; cout << "你需要通过三道试炼,找回并合成它。\n\n"; press_enter(); } // level3.cpp #include <iostream> #include <vector> #include <algorithm> #include <random> using namespace std; struct Fragment { int id, len, wt; }; bool play_level3() { cout << "\n--- 第三关:合成魔杖\n"; cout << "--- 每块碎片需放入魔法炉打磨。\n"; cout << "--- 初始开启需 1 天准备时间。\n"; cout << "--- 若下一块碎片长度和重量都不小于上一块,则无需重新准备;\n"; cout << "--- 否则需再花 1 天准备时间。\n"; cout << "--- “我发现只有一个小碎片与众不同……也许它应该留在最后”\n"; // --- 固定碎片属性,使 5->3->4->1->2 是唯一最优路径 --- vector<Fragment> frags = { {1, 6, 5}, // 碎片1: L=6, W=5 {2, 7, 8}, // 碎片2: L=7, W=8 (放最后会断链) {3, 4, 3}, // 碎片3: L=4, W=3 {4, 5, 6}, // 碎片4: L=5, W=6 {5, 3, 2} // 碎片5: L=3, W=2 (起点) }; // 随机打乱显示顺序(让玩家看不出规律) random_device rd; mt19937 rng(rd()); shuffle(frags.begin(), frags.end(), rng); // 显示当前碎片信息 cout << "--- 碎片属性如下:\n"; for (const auto& f : frags) { cout << " 碎片 " << f.id << ": L=" << f.len << ", W=" << f.wt << "\n"; } cout << "\n--- 请安排打磨顺序(输入碎片编号1~5):\n"; // --- 步骤:用回溯法找出理论最小天数 --- vector<int> indices = { 0, 1, 2, 3, 4 }; int min_days = 10; do { int days = 1; for (int i = 1; i < 5; ++i) { auto& prev = frags[indices[i - 1]]; auto& curr = frags[indices[i]]; if (!(curr.len >= prev.len && curr.wt >= prev.wt)) { days++; } } if (days < min_days) { min_days = days; } } while (next_permutation(indices.begin(), indices.end())); // --- 玩家尝试两次 --- for (int attempt = 1; attempt <= 2; ++attempt) { cout << "\n--- 第 " << attempt << " 次尝试\n"; cout << "请输入顺序(五个数字):"; vector<int> input(5); bool valid = true; for (int i = 0; i < 5; ++i) { cin >> input[i]; if (input[i] < 1 || input[i] > 5) { cout << "--- 错误:请输入1~5之间的数字!\n"; valid = false; break; } } if (!valid) { if (attempt == 1) cout << "--- 请再试一次。\n"; continue; } // 检查重复 vector<int> tmp = input; sort(tmp.begin(), tmp.end()); if (unique(tmp.begin(), tmp.end()) != tmp.end()) { cout << "--- 错误:不能重复选择!\n"; if (attempt == 1) cout << "--- 请再试一次。\n"; continue; } // 构造实际序列 vector<Fragment> seq; for (int id : input) { for (auto f : frags) { if (f.id == id) { seq.push_back(f); break; } } } // 计算耗时 int days = 1; cout << "\n--- 开始合成...\n"; cout << " 第1块(" << seq[0].id << "): 启动,耗时1天。\n"; for (int i = 1; i < 5; ++i) { if (seq[i].len >= seq[i - 1].len && seq[i].wt >= seq[i - 1].wt) { cout << " 第" << i + 1 << "块(" << seq[i].id << "): 规格不降,连续打磨。\n"; } else { days++; cout << " 第" << i + 1 << "块(" << seq[i].id << "): 规格下降,+1天。\n"; } } cout << "--- 实际总耗时:" << days << " 天。\n"; // 判断是否成功:必须是最优且顺序为 5 3 4 1 2 if (days == min_days) { // 再检查是不是恰好是 5 3 4 1 2 if (input[0] == 5 && input[1] == 3 && input[2] == 4 && input[3] == 1 && input[4] == 2) { cout << "\n--- 成功!你找到了唯一的最优顺序!\n"; return true; } else { cout << "--- 虽然耗时最少,但这不是预期的合成方式……\n"; cout << "--- 也许那句提示另有深意?\n"; if (attempt == 1) cout << "--- 请再试一次。\n"; } } else { cout << "--- 时间太长了,还有更好的顺序。\n"; if (attempt == 1) cout << "--- 请再试一次。\n"; } } cout << "\n--- 很遗憾,挑战失败。\n"; return false; } ////答案53412#pragma once // level3.h #ifndef LEVEL3_H #define LEVEL3_H bool play_level3(); #endif “合成 ”游戏单元的实现 采用的算法名称: 求解过程: 算法实现,推荐用PAD图描述
最新发布
10-25
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值