P1938 [USACO09NOV]Job Hunt S(spfa,队列+邻接表优化)

奶牛贝茜在寻找工作时面临城市赚钱限制,每个城市最多赚D美元。存在免费路径和付费航班。问题转化为求最长负权路径。通过SPFA算法找出可能的最短路径,如果存在负权环则无法确定最短路径,输出-1。否则,计算总收益并返回结果。给定示例中,贝茜能赚到250美元。

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

题目描述

Bessie is running out of money and is searching for jobs. Farmer John knows this and wants the cows to travel around so he has imposed a rule that his cows can only make D (1 <= D <= 1,000) dollars in a city before they must work in another city. Bessie can, however, return to a city after working elsewhere for a while and again earn the D dollars maximum in that city. There is no limit on the number of times Bessie can do this.

Bessie's world comprises P (1 <= P <= 150) one-way paths connecting C (2 <= C <= 220) cities conveniently numbered 1..C. Bessie is currently in city S (1 <= S <= C). Path i runs one-way from city A_i to city B_i (1 <= A_i <= C; 1 <= B_i <= C) and costs nothing to traverse.

To help Bessie, Farmer John will give her access to his private jet service. This service features F (1 <= F <= 350) routes, each of which is a one way flight from one city J_i to a another K_i (1 <= J_i <= C; 1 <= K_i <= C) and which costs T_i (1 <= T_i <= 50,000) dollars. Bessie can pay for the tickets from future earnings if she doesn't have the cash on hand.

Bessie can opt to retire whenever and wherever she wants. Given an unlimited amount of time, what is the most money that Bessie can make presuming she can make the full D dollars in each city she can travel to? Print -1 if there is no limit to this amount.

奶牛们正在找工作。农场主约翰知道后,鼓励奶牛们四处碰碰运气。而且他还加了一条要求:一头牛在一个城市最多只能赚D(1≤D≤1000)美元,然后它必须到另一座城市工作。当然,它可以在别处工作一阵子后又回到原来的城市再最多赚D美元。而且这样的往返次数没有限制。

城市间有P(1≤P≤150)条单向路径连接,共有C(2≤C≤220)座城市,编号从1到C。奶牛贝茜当前处在城市S(1≤S≤C)。路径i从城市A_i到城市B_i(1≤A_i≤C,1≤B_i≤C),在路径上行走不用任何花费。

为了帮助贝茜,约翰让它使用他的私人飞机服务。这项服务有F条(1≤F≤350)单向航线,每条航线是从城市J_i飞到另一座城市K_i(1≤J_i≤C,1≤K_i≤C),费用是T_i(1≤T_i≤50000)美元。如果贝茜手中没有现钱,可以用以后赚的钱来付机票钱。

贝茜可以选择在任何时候,在任何城市退休。如果在工作时间上不做限制,贝茜总共可以赚多少钱呢?如果赚的钱也不会出现限制,就输出-1。

输入格式

第一行:5个用空格分开的整数D,P,C,F,S。

第2到第P+1行:第i+1行包含2个用空格分开的整数,表示一条从城市A_i到城市B_i的单向路径。

接下来F行,每行3个用空格分开的整数,表示一条从城市J_i到城市K_i的单向航线,费用是T_i。

输出格式

一个整数,在上述规则下最多可以赚到的钱数。

输入输出样例

输入 #1复制

100 3 5 2 1
1 5
2 3
1 4
5 2 150
2 5 120

输出 #1复制

250

说明/提示

This world has five cities, three paths and two jet routes. Bessie starts out in city 1, and she can only make 100 dollars in each city before moving on.

Bessie can travel from city 1 to city 5 to city 2 to city 3, and make a total of 4*100 - 150 = 250 dollars.

Source: USACO 2009 November Silver

这个世界上有五个城市,三条单向路径和两条单向航线。贝茜从一号城市开始她的旅行,她在离开一个城市前最多只能在这个城市赚100美元。

贝茜可以通过从一号城市-->五号城市-->二号城市-->三号城市的旅行赚到4*100-150=250美元。

(注:在四个城市各赚100美元,从五号城市飞到二号城市花掉150美元)

来源:USACO 2009 十一月银组

分析:

把d转化为 边权值,即求最长路径,再取负得最短路径 ,spfa处理;

有负权环说明没有最短路径,输出-1;

#include <bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int d;
int m;int n;
int f;
int s;
int dis[290];
int vis[290];
int cnt[290];
vector<pair<int,int>>v[290];
int spfa(){
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;
    queue<int>q;  //队列,int型,存放节点编号
    q.push(s);
    vis[s]=1;
    int i;
    while(!q.empty()){                     //1 5 -100
        int cur=q.front();                 //2 3 -100
        q.pop();                            //1 4 -100
        vis[cur]=0;                         //5 2 50
        cnt[cur]++;                         //2 5 20
        if(cnt[cur]>=n){//一个点进队到达n次说明有负权环,也就没有最短路径
            cout<<-1;
            return 0;
        }
        for(i=0;i<(int)v[cur].size();i++){
            if(dis[v[cur][i].first]>dis[cur]+v[cur][i].second){
                dis[v[cur][i].first]=dis[cur]+v[cur][i].second;
                if(vis[v[cur][i].first]==0){
                    vis[v[cur][i].first]=1;
                    q.push(v[cur][i].first);
                }
            }
        }
    }
    return 1;
}
int main (){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>d>>m>>n>>f>>s;
    int i,j;
    for(i=1;i<=m;i++){
        int x,y;
        cin>>x>>y;
        v[x].emplace_back(y,-d);
    }
    for(i=1;i<=f;i++){
        int x,y,w;
        cin>>x>>y>>w;
        v[x].emplace_back(y,-d+w);
    }
    int res=inf;
    if(spfa()){
        for(i=1;i<=n;i++){
            res=min(res,dis[i]);
        }
        res=-res;
        res+=d;
        cout<<res;
    }
    return 0;
}

### 解决方案 USACO 的题目 **P2895 Meteor Shower S** 是一道经典的 BFS(广度优先搜索)问题,涉及路径规划以及动态障碍物的处理。以下是关于此题目的 C++ 实现方法及相关讨论。 #### 1. 题目概述 贝茜需要在一个二维网格上移动到尽可能远的位置,同时避开由流星造成的破坏区域。每颗流星会在特定时间落在某个位置,并摧毁其周围的五个单元格(中心及其上下左右)。目标是最小化贝茜受到的风险并计算最短到达安全地点的时间[^5]。 --- #### 2. 关键算法思路 为了高效解决这个问题,可以采用以下策略: - 使用 **BFS(广度优先搜索)** 来模拟贝茜可能的行走路线。 - 动态更新地图上的危险区域,确保在每个时刻只考虑有效的威胁。 - 提前预处理所有流星的影响范围,减少冗余计算。 由于直接在每次 BFS 中调用 `boom` 函数可能导致性能瓶颈[^4],因此可以通过优化来降低复杂度。 --- #### 3. 优化建议 为了避免重复标记已知的危险区域,可以在程序初始化阶段完成如下操作: - 创建一个数组记录每个单位时间内哪些坐标会被流星影响。 - 将 BFS 和流星爆炸事件同步进行,仅在必要时扩展新的状态。 这种方法能够显著提升运行速度,尤其对于大规模输入数据(如 $ M \leq 50,000 $),效果尤为明显。 --- #### 4. C++ 示例代码实现 下面提供了一个高效的解决方案框架: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 1e6; int grid[1001][1001]; // 地图大小假设为合理范围内 bool visited[1001][1001]; queue<pair<int, pair<int, int>>> q; // 存储 {time, {x, y}} // 方向向量定义 vector<pair<int, int>> directions = { {-1, 0}, {1, 0}, {0, -1}, {0, 1} }; void initializeGrid(int N, vector<tuple<int, int, int>>& meteors) { memset(grid, 0, sizeof(grid)); for(auto &[t, x, y] : meteors){ if(t >= N || t < 0) continue; // 超过最大时间或负数忽略 grid[x][y] = max(grid[x][y], t); for(auto &[dx, dy] : directions){ int nx = x + dx, ny = y + dy; if(nx >=0 && nx <1001 && ny>=0 && ny<1001){ grid[nx][ny] = max(grid[nx][ny], t); } } } } bool isValid(int time, int x, int y){ return !(grid[x][y] <= time); // 如果当前时间<=流星爆炸时间则不可通过 } int main(){ ios::sync_with_stdio(false); cin.tie(0); int T, X, Y; cin >> T >> X >> Y; vector<tuple<int, int, int>> meteors(T); for(int i=0;i<T;i++) cin >> get<0>(meteors[i]) >> get<1>(meteors[i]) >> get<2>(meteors[i]); initializeGrid(X*Y, meteors); memset(visited, false, sizeof(visited)); q.push({0,{X,Y}}); visited[X][Y]=true; while(!q.empty()){ auto current = q.front(); q.pop(); int currentTime = current.first; int cx = current.second.first, cy = current.second.second; if(isValid(currentTime,cx,cy)){ cout << currentTime; return 0; } for(auto &[dx,dy]:directions){ int nx=cx+dx,ny=cy+dy; if(nx>=0&&nx<1001&&ny>=0&&ny<1001&&!visited[nx][ny]){ if(isValid(currentTime,nx,ny)){ q.push({currentTime+1,{nx,ny}}); visited[nx][ny]=true; } } } } cout << "-1"; // 若无解返回-1 return 0; } ``` 上述代码实现了基于 BFS 的最优路径查找逻辑,并预先构建了流星影响的地图以加速查询过程。 --- #### 5. 进一步讨论 尽管本题的核心在于 BFS 及动态更新机制的应用,但在实际编码过程中仍需注意以下几个方面: - 输入规模较大时应选用快速 IO 方法(如关闭同步流 `ios::sync_with_stdio(false)` 并取消绑定 `cin.tie(NULL)`)。 - 对于超出边界或者无关紧要的数据点可以直接跳过处理,从而节省不必要的运算开销。 - 利用位掩码或其他压缩技术存储访问标志可进一步节约内存资源。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值