Codeforces#374(div2)- C. Journey(二分答案+dp)

本文介绍了一种解决CodeForces平台C题的有效方法。通过使用二分查找确定最多能经过的点数,并借助动态规划(DP)进行验证。文章详细解释了状态表示、转移方程及边界条件等关键步骤,并提供了完整的C++代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

http://codeforces.com/contest/721/problem/C

思路

二分最多能够经过多少个点,然后dp判断
状态表示
d[i][j] :当前在第i个点,经过了j个点的最小距离
转移方程
d[i][j]=min(d[k][j1]+w(i,k),d[i][j]) (k是i的前驱,w(k, i)表示k到i的边长)
边界条件
当i == 1时,只要 j1 即可,即走到起点的时候,实际经过的点的数目可以不到j

细节

  1. 二分的时候L = 1,如果L = 2的话不会进入dp,因此不会记录路径
  2. 记录路径的时候也需要开2维
  3. 建图的时候边要反向,因为找的是前驱

代码

#include <iostream>
#include <cstring>
#include <stack>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <sstream>
#include <iomanip>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <climits>
#include <deque>
#include <bitset>
#include <algorithm>
using namespace std;

#define PI acos(-1.0)
#define LL long long
#define PII pair<int, int>
#define PLL pair<LL, LL>
#define mp make_pair
#define IN freopen("in.txt", "r", stdin)
#define OUT freopen("out.txt", "wb", stdout)
#define scan(x) scanf("%d", &x)
#define scan2(x, y) scanf("%d%d", &x, &y)
#define scan3(x, y, z) scanf("%d%d%d", &x, &y, &z)
#define sqr(x) (x) * (x)

const int maxn = 5000 + 5;
const int INF = 0x3e3e3e3e;
struct Edge {
    int from, to, dist;
    Edge(int u, int v, int w) : from(u), to(v), dist(w) {

    }
};

vector<Edge> edges;
vector<int> G[maxn];
int n, m, T;

void addedge(int u, int v, int w) {
    edges.push_back(Edge(u, v, w));
    G[u].push_back(edges.size() - 1);
}

int d[maxn][maxn], pa[maxn][maxn];
int dp(int i, int j) {
    if (d[i][j] != -1) return d[i][j];
    if (i == 1) return d[i][j] = j <= 1 ? 0 : INF;
    d[i][j] = INF;
    for (int ii = 0; ii < G[i].size(); ii++) {
        Edge e = edges[G[i][ii]];
        int k = e.to;
        int t = dp(k, j - 1) + e.dist;
        if (t < d[i][j]) {
            d[i][j] = t;
            pa[i][j] = k;
        }
    }
    return d[i][j];
}

bool judge(int x) {
    memset(d, -1, sizeof(d));
    int t = dp(n, x);
    return t <= T;
}

void print(int u, int x) {
    if (u == 1) return;
    print(pa[u][x], x - 1);
    printf(" %d", u);
}

int main() {
    scan3(n, m, T); 
    for (int i = 0; i < m; i++) {
        int x, y, z;
        scan3(x, y, z);
        addedge(y, x, z);
    }
    int L = 1, R = n, M = (L + R) >> 1;
    while (L < R) {
        if (R == L + 1) {
            if (judge(R)) M = R;
            else {
                judge(L);
                M = L;
            }
            break;
        } else {
            M = (L + R) >> 1;
            if (judge(M)) L = M;
            else R = M;
        }
    }
    printf("%d\n", M);
    printf("1");
    print(n, M);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值