#洛谷 P1113 杂物 (带限制最短路)

题目描述

John的农场在给奶牛挤奶前有很多杂务要完成,每一项杂务都需要一定的时间来完成它。比如:他们要将奶牛集合起来,将他们赶进牛棚,为奶牛清洗乳房以及一些其它工作。尽早将所有杂务完成是必要的,因为这样才有更多时间挤出更多的牛奶。当然,有些杂务必须在另一些杂务完成的情况下才能进行。比如:只有将奶牛赶进牛棚才能开始为它清洗乳房,还有在未给奶牛清洗乳房之前不能挤奶。我们把这些工作称为完成本项工作的准备工作。至少有一项杂务不要求有准备工作,这个可以最早着手完成的工作,标记为杂务111。John有需要完成的nnn个杂务的清单,并且这份清单是有一定顺序的,杂务k(k>1)k(k>1)k(k>1)的准备工作只可能在杂务111至k−1k-1k−1中。

写一个程序从111到nnn读入每个杂务的工作说明。计算出所有杂务都被完成的最短时间。当然互相没有关系的杂务可以同时工作,并且,你可以假定John的农场有足够多的工人来同时完成任意多项任务。

输入格式

第1行:一个整数nnn,必须完成的杂务的数目(3≤n≤10,0003 \le n \le 10,0003≤n≤10,000);

第222至(n+1)(n+1)(n+1)行: 共有nnn行,每行有一些用111个空格隔开的整数,分别表示:

* 工作序号(111至nnn,在输入文件中是有序的);

* 完成工作所需要的时间len(1≤len≤100)len(1 \le len \le 100)len(1≤len≤100);

* 一些必须完成的准备工作,总数不超过100100100个,由一个数字000结束。有些杂务没有需要准备的工作只描述一个单独的000,整个输入文件中不会出现多余的空格。

输出格式

一个整数,表示完成所有杂务所需的最短时间。

输入输出样例

输入 #1

7
1 5 0
2 2 1 0
3 3 2 0
4 6 1 0
5 1 2 4 0
6 8 2 4 0
7 4 3 5 6 0

输出 #1复制

23

题目大意 : 输入一个N, 接下来有N行, 每行前两个数代表序号和该序号完工需要花费的时间, 接下来输入一个数直到他 == 0, 表示只有完成这个数的工作完成才能进行该开头输入的数的工作, 输出完成所有工作的最短时间

思路 : 写完这题后看了下题解,几乎没有我这么做的, 基本都是DP、拓扑排序,不过想法大同小异。 之前也做过一道类似的带限制最短路的题, 基本思路就是, 当一个点不受限制后就将他入队, 在输入的过程中, 把每个点都处理下并建立超级源点和超级汇点(防止一开始可以进行很多工作和很多工作可能同时结束),如果这个点没有限制他的点, 那么就将他与源点相连, 如果某个点不限制其他的点, 那么一定是最后结束的,将他和汇点相连,观察后可以发现,每个点的最短完成时间,一定是限制他的所有点的完成时间的最大值 + 该点的完工时间, 这个就是最短路的更新条件。 一切处理完毕, 直接从源点到汇点跑一次最短路,就解决了。

Accepted code

#include<bits/stdc++.h>
using namespace std;

#define sc scanf
#define ls rt << 1
#define rs ls | 1
#define Min(x, y) x = min(x, y)
#define Max(x, y) x = max(x, y)
#define ALL(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
#define MEM(x, b) memset(x, b, sizeof(x))
#define lowbit(x) ((x) & (-x))
#define P2(x) ((x) * (x))

typedef long long ll;
const int MOD = 1e9 + 7;
const int MAXN = 1e5 + 10;
const int INF = 0x3f3f3f3f;
inline ll fpow(ll a, ll b){ ll r = 1, t = a; while (b){ if (b & 1)r = (r*t) % MOD; b >>= 1; t = (t*t) % MOD; }return r; }

struct node
{
	int id, w;
	bool operator < (const node &oth) const
	{
		return w > oth.w;
	}
}mid;
int n, m, sp, tp;
int dis[MAXN], in[MAXN], val[MAXN];
vector <int> e[MAXN << 1];
vector <int> edge[MAXN << 1];
void dijkstra(int u) {
	MEM(dis, INF); priority_queue <node> q;
	q.push({ u, 0 }); dis[u] = 0;
	while (!q.empty()) {
		mid = q.top();
		q.pop();
		int ans = mid.id;
		if (dis[ans] != mid.w) continue;
		for (int i = 0; i < e[ans].size(); i++) {
			int vi = e[ans][i];
			in[vi]--;
			if (!in[vi]) { // 没有限制就入队
				int max_ = -1;
				for (int j = 0; j < edge[vi].size(); j++)
					max_ = max(max_, dis[edge[vi][j]] + val[vi]);
				dis[vi] = max_; // 更新最短路
				q.push({ vi, dis[vi] });
			}
		}
	}
}

int main()
{
	cin >> n; 
	sp = 0, tp = n + 1;
	for (int i = 1; i <= n; i++) {
		int ui, vi, wi;
		sc("%d %d", &ui, &wi);
		val[ui] = wi;
		int tot = 0;
		while (sc("%d", &vi) && vi) {
			tot++; in[ui]++;
			e[vi].push_back(ui);
			edge[ui].push_back(vi);
		}
		if (!tot){ // 不被其他店限制,一定要先走
			e[sp].push_back(i);
			edge[i].push_back(sp);
			in[i]++;
		}
	}
	for (int i = 1; i <= n; i++) { // 不限制其他点, 一定最后走
		if (e[i].size() == 0) {
			e[i].push_back(tp);
			edge[tp].push_back(i);
			in[tp]++;
		}
	}
	dijkstra(sp);
	cout << dis[tp] << endl;
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值