P4822 [BJWC2012]冻结 (分层图最短路)

该博客讨论了如何解决P4822编程题目,即在一个包含N个城市和M条双向道路的网络中,从城市1到城市N寻找最短路径。问题中引入了K张能将通行时间减半的SpellCard,每条道路最多使用一次。博主分享了如何在考虑SpellCard使用策略的情况下,找到从起点到终点的最短时间。文章包含问题描述和代码实现。

题目:https://www.luogu.org/problem/P4822

有 N 个城市,M 条双向的道路。城市编号为 1~N,我们在 1 号城市,需要到 N 号城市,怎样才能最快地到达呢?

现在,我们一共有 K 张可以使时间变慢 50%的 SpellCard,也就是说,在通过某条路径时,我们可以选择使用一张卡片,这样,我们通过这一条道路的时间 就可以减少到原先的一半。需要注意的是:

  1. 在一条道路上最多只能使用一张 SpellCard。
  2. 使用一张SpellCard 只在一条道路上起作用。
  3. 你不必使用完所有的 SpellCard。

给定以上的信息,你的任务是:求出在可以使用这不超过 K 张时间减速的 SpellCard 之情形下,从城市1 到城市N最少需要多长时间。

 

 代码:

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int, int>
const int INF = 0x3f3f3f3f;
const int MAXN = 3000;
const int MAXM = 1e5 * 3;
int n, m, k;
struct Edge
{
    int to, w, nxt;
}edge[MAXM];
int head[MAXN], t = 1;
void init()
{
    memset(head, -1, sizeof(head));
    t = 1;
}
void add(int u, int v, int w)
{
    edge[t].to = v;
    edge[t].w = w;
    edge[t].nxt = head[u];
    head[u] = t++;
}
int dis[MAXN], book[MAXN];
void dijkstra()
{
    memset(dis, INF, sizeof(dis));
    memset(book, 0, sizeof(book));

    dis[1] = 0;

    priority_queue<pii, vector<pii>, greater<pii> > q;
    q.push({dis[1],1});
    while(!q.empty())
    {
        int u = q.top().second;
        q.pop();

        if(book[u]) continue;
        book[u] = 1;

        for(int i = head[u]; i != -1; i = edge[i].nxt)
        {
            int to = edge[i].to;
            int w = edge[i].w;

            if(!book[to] && dis[to] > dis[u] + w)
            {
                dis[to] = dis[u] + w;
                q.push({dis[to], to});
            }
        }
    }
}
int main()
{
    cin >> n >> m >> k;
    init();
    int a, b, c;
    while(m--)
    {
        cin >> a >> b >> c;
        for(int i = 0; i <= k; i++)
        {
            add(a + i * n, b + i * n, c);
            add(b + i * n, a + i * n, c);
            if(i != k)
            {
                add(a + i * n, b + (i + 1) * n, c/2);
                add(b + i * n, a + (i + 1) * n, c/2);
            }
        }
    }
    dijkstra();
    int ans = INF;
    for(int i = 0; i <= k; i++)
    {
       // cout << dis[n + i * n] << endl;
        ans = min(ans, dis[n + i * n]);
    }
    cout << ans << endl;
    return 0;
}

 

### BJWC2018 长上升子序列问题解析 对于BJWC2018长上升子序列问题,目标是在给定的一个长度为n的随机排列中找到其长上升子序列(LIS)的期望长度,并输出该值模998244353的结果[^2]。 #### 动态规划方法求解LIS 一种经典的方法是通过动态规划(DP)来解决这个问题。设`dp[i]`表示以第i个元素结尾的大升序子序列的长度,则状态转移方程可以定义如下: 如果存在某个位置j<i使得a[j]<a[i], 则有 `dp[i]=max(dp[i], dp[j]+1)`. 初始化时,所有的`dp[i]`都设置为1,因为短的情况就是一个单独的数字构成的序列。终的答案将是所有可能的`dp[]`中的大值。 然而,在本题的情况下,由于需要计算的是期望而不是确切的大长度,因此还需要引入概率论的知识来进行调整。 ```cpp const long long MOD = 998244353; vector<long long> lis(vector<int>& nums){ vector<long long> res(nums.size(), 1); for (int i=1; i<nums.size(); ++i){ for(int j=0;j<i;++j){ if(nums[j]<nums[i]){ res[i]= max(res[i]%MOD,(res[j]+1)%MOD ); } } } return res; } ``` 这段代码实现了上述提到的状态转移逻辑,但是它并没有直接解决问题的要求——即求解期望值。为了得到正确的答案,还需进一步处理这些中间结果并考虑如何将其转换成所需的期望形式。 实际上,此题目的精确解答涉及到组合数学以及复杂的计数技巧,超出了简单的DP框架所能覆盖的内容。具体实现通常依赖于预处理阶乘逆元等高级技术以便高效地完成大范围内数值的概率运算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值