【[Ynoi2015]我回来了】

人生第一道ynoi题,感觉大家是不是被ynoi的标签吓到了啊= =,此题的难度个人觉得不到黑题。

正文部分

\(bitset\)+类似一个\(dp\)的东西乱搞。
定义\(f[u][x]\)的含义是从第\(u\)号点开始所有路径长度不超过\(x\)的集合,这个东西显然我们是可以用\(bitset\)去维护的。
接着我们考虑在这个\(f\)数组上动一些手脚
对于任何一个\(f[i][j]\),其\(f[i][dis[j]][j]=1\)
这个东西是固定的,为什么?因为是最短路,到达了这个路径的最后一步一定是这个点,于是必定为\(1\)
继续考虑转移\(f[i][j]\)
不难发现:\(f[i][j]=f[i][j]|f[i][j-1]\)

其中\(j\)表示的是一个距离,为什么可以这样转移?因为对于任何一个\(dis\),其\(dis-1\)包含了\(dis-2\),\(dis-2\)包含了\(dis-3\)...,于是我们可以大胆的直接或上就行了。

至于最短路,因为权值都相等,所以直接可以写\(1\)\(bfs\)这里省略。我不会告诉你我dij都写完了才发现了logn的大常数。

My Code:

#include <bits/stdc++.h>
#define il inline
#define gc getchar
#define pc putchar
#define pb push_back
const int MAXN = 1010;
const int GINF = 1001;
using namespace std;
namespace IO {
    il int read() {
        int res = 0;char c;bool sign = 0;
        for(c = gc();!isdigit(c);c = gc()) sign |= c == '-';
        for(;isdigit(c);c = gc()) res = (res << 1) + (res << 3) + (c ^ 48);
        return sign ? -res : res;
    }
    il void write(int x) {
        if(x < 0) x = -x,pc('-');
        int stk[20];stk[0] = 0;
        for(;x;x /= 10) stk[++stk[0]] = x % 10;
        for(int i = stk[0];i >= 1;i--) pc(stk[i] + 48);
        return;
    }
    il void writeln(int x) {
        write(x);pc('\n');
    }
}
using IO::read;using IO::write;using IO::writeln;
int n,m,i,j,k,q,_i;
bitset<MAXN> f[MAXN][MAXN];
int dis[MAXN];vector<int>g[MAXN];
il void readdEdge(int u,int v) {
    g[u].pb(v);g[v].pb(u);
}
il void bfs(int S) {
    int i;
    for(i = 1;i <= n;i++) dis[i] = GINF;dis[S] = 0;
    queue<int>Q;Q.push(S);
    while(!Q.empty()) {
        int u = Q.front();Q.pop();
        for(i = 0;i < g[u].size();i++) {
            int to = g[u][i];
            if(dis[u] + 1 < dis[to]) {
                dis[to] = dis[u] + 1;
                Q.push(to);
            }
        }
    }
//  cout << endl << S;
//  for(i = 1;i <= n;i++) cout << dis[i] << ' ';
//  cout << endl;
    return;
} 

int main() {
    n = read();m = read();q = read();
    for(i = 1;i <= m;i++) {
        int u = read(),v = read();
        readdEdge(u,v);
    }   
    for(i = 1;i <= n;i++) {
        bfs(i);
        for(j = 1;j <= n;j++) f[i][dis[j]].set(j);
        for(j = 1;j <= n;j++) f[i][j] |= f[i][j - 1];
    }
    for(_i = 1;_i <= q;_i++) {
        int T = read(),_j;bitset<MAXN> res;
        for(_j = 1;_j <= T;_j++) {
            int x = read(),y = read();
            res |= f[x][y];
        }
        writeln(res.count());
    }
    return 0;
}

转载于:https://www.cnblogs.com/Sai0511/p/10360584.html

题目 P5413 "YNOI2019 骑单车" 是一个经典的动态规划和贪心算法的问题。该题主要涉及两个概念:路径规划和状态转移方程。 **背景描述** 假设你在一个二维网格上,每个单元格代表一个地点,你需要从起点出发骑车到终点,并尽可能地减少骑行时间。网格中的每个单元格都有两种可能的状态:平地(速度不变)或斜坡(速度减半)。你的目标是找到一条最短的路线。 **关键点解析** 1. **动态规划**:通常用于求解最优化问题。在这个问题中,我们可以定义一个二维数组 dp[i][j] 表示从起点到位置 (i, j) 的最短行驶时间。状态转移方程会根据当前位置的性质(平地还是斜坡)以及到达此位置的最短路径来自之前的节点计算。 2. **状态转移**:对于平地,dp[i][j] = dp[pi][pj] + cost,表示直接移动到相邻位置的时间;对于斜坡,dp[i][j] = min(dp[pi][pj], dp[pi][pj-1]) + cost/2,因为斜坡速度减半,所以需要选择更早的时刻经过。 3. **贪心策略**:有时候,为了达到全局最优,初始看起来不是最优的选择可能是正确的。但在这个问题中,贪心策略可能并不适用,因为我们不能仅依据当前状态做出决策,需要考虑到整个路径。 4. **边界条件**:初始化 dp 数组时,起点时间设为 0,其余位置设为正无穷大,保证一开始就只会向可达的位置移动。 **代码实现** 实现这样的动态规划算法通常需要用到一个优先队列(如最小堆),以便于高效地查找之前节点的最优时间。 **相关问题--:** 1. 如何设计状态转移方程来处理平地和斜坡的情况? 2. 这个问题是否存在剪枝操作以提高效率? 3. 如果网格大小非常大,如何避免存储所有 dp 值导致的空间爆炸?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值