洛谷 P1113 杂务

news/2024/7/15 14:28:45 标签: 算法, 数据结构, 图论

【题目链接】

洛谷 P1113 杂务

【题目考点】

1. 图论:拓扑排序

2. 动规:DAG图上动规

【解题思路】

首先对该问题进行抽象,每项杂务是一个顶点,如果杂务A是杂务B的准备工作,那么A到B有一条有向边。整个图是有向图。
设数组len,len[i]表示完成杂务i所需要的时长。

解法1:拓扑排序

写法1:设数组表示杂务的完成时刻

假设这个人在工作中不会休息,完成一项杂务后,会立刻去做下一项杂务。
设数组fin,fin[i]表示杂务i的完成时刻。

按照拓扑排序顺序,分别求每项杂务的完成时刻。
对于入度为0的顶点i,该杂务的完成时刻就是完成该杂务的时长len[i]
每确定一项杂务u的完成时刻,就更新该顶点u的邻接点v的完成时刻。
已知u的完成时刻为fin[u],v应该在u完成后再开始,这样的话v的完成时刻为fin[u]+len[v]
对于v的每个准备工作u,都可以推出v的完成时刻为fin[u]+len[v]。而v必须在所有准备工作完成后才能开工,因此v的完成时刻fin[v]应该为所有求出的fin[u]+len[v]的最大值。即更新写法为:fin[v] = max(fin[v], fin[u]+len[v])

最后求出所有杂务完成时刻的最大值,即为完成所有任务需要的时间。

写法2:设数组表示杂务的开始时刻

另一种方法,可以设数组startstart[i]表示杂务i的开工时刻。在确定u的开工时刻start[u]后,确定u的邻接点v的开工时刻,写为:start[v] = max(start[v], start[u]+len[u])

最后求出所有杂务开始时刻加任务时长的最大值,即为完成所有任务需要的时间。

解法2:图上动规

题目中说:“杂务k的准备工作只可能在杂务1至k-1中。”即便没有这个条件,我们也可以先做拓扑排序,得到的序列一定满足序列中第i项工作的准备工作都在第1至第i-1项工作中。

既然有这一条件,就无需先做拓扑排序了。建图时,改为建反图,即如果杂务A是杂务B的准备工作,那么让B到A有一条有向边。
在这样的图中,一个顶点的邻接点就是它的准备工作。
设数组fin,fin[i]表示杂务i的完成时刻。
当按照给定序列顺序,需要求fin[u]时,由于u的准备工作v在序列中排在u的前面,所以此时一定已经求出了fin[v]fin[v]是已知的。求所有u的准备工作的结束时间的最大值,即为u的开始时刻,再加上完成u的时间len[u],就得到了完成u的最早时刻fin[u]
同理,也可以设任务的开始时刻来完成该题。

【题解代码】

解法1:拓扑排序

  • 写法1:设数组表示杂务的完成时刻
#include <bits/stdc++.h>
using namespace std;
#define N 10005
vector<int> edge[N];
int n, degIn[N], len[N], fin[N], mxFin;//len[i]:任务i的完成时长 fin[i]:任务i最早的完成时间 
void topoSort()
{
	queue<int> que;
	for(int v = 1; v <= n; ++v)
		if(degIn[v] == 0)
		{
			que.push(v);
			fin[v] = len[v];
		}
	while(que.empty() == false)
	{
		int u = que.front();
		que.pop();
		for(int v : edge[u])
		{
			fin[v] = max(fin[v], fin[u]+len[v]);
			if(--degIn[v] == 0)
				que.push(v);
		}
	}
}
int main()
{
	int a, t;
	cin >> n;
	for(int f = 1; f <= n; ++f)
	{
		cin >> a >> len[f];
		while(cin >> t && t != 0)
		{
			edge[t].push_back(f);
			degIn[f]++;
		}
	}
	topoSort();
	for(int v = 1; v <= n; ++v)
		mxFin = max(mxFin, fin[v]);
	cout << mxFin;
	return 0;
}
  • 写法2:设数组表示杂务的开始时刻
#include <bits/stdc++.h>
using namespace std;
#define N 10005
vector<int> edge[N];
int n, degIn[N], len[N], start[N], mxFin;//len[i]:任务i的完成时长 start[i]:任务i最早的开始时间 
void topoSort()
{
	queue<int> que;
	for(int v = 1; v <= n; ++v)
		if(degIn[v] == 0)
			que.push(v);
	while(que.empty() == false)
	{
		int u = que.front();
		que.pop();
		for(int v : edge[u])
		{
			start[v] = max(start[v], start[u]+len[u]);
			if(--degIn[v] == 0)
				que.push(v);
		}
	}
}
int main()
{
	int a, t;
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		cin >> a >> len[i];
		while(cin >> t && t != 0)
		{
			edge[t].push_back(i);
			degIn[i]++;
		}
	}
	topoSort();
	for(int v = 1; v <= n; ++v)
		mxFin = max(mxFin, start[v]+len[v]);
	cout << mxFin;
	return 0;
}

解法2:图上动规

  • 写法1:设数组表示杂务的完成时刻
#include <bits/stdc++.h>
using namespace std;
#define N 10005
vector<int> edge[N];
int n, len[N], fin[N], mxFin;//len[i]:任务i的完成时长 fin[i]:任务i最早的开始时间 
int main()
{
	int a, t;
	cin >> n;
	for(int f = 1; f <= n; ++f)
	{
		cin >> a >> len[f];
		fin[f] = len[f];//先将每个任务的结束时间设为完成时长 
		while(cin >> t && t != 0)
			edge[t].push_back(f);//如果f是t的准备工作,那么存在弧<t, f> 
	}
	for(int u = 1; u <= n; ++u)
		for(int v : edge[u])//找所有u的准备工作
			fin[v] = max(fin[v], fin[u]+len[v]);
	for(int v = 1; v <= n; ++v)
		mxFin = max(mxFin, fin[v]);
	cout << mxFin;
	return 0;
}
  • 写法2:设数组表示杂务的开始时刻
#include <bits/stdc++.h>
using namespace std;
#define N 10005
vector<int> edge[N];
int n, len[N], start[N], mxFin;//len[i]:任务i的完成时长 start[i]:任务i最早的开始时间 
int main()
{
	int a, t;
	cin >> n;
	for(int f = 1; f <= n; ++f)
	{
		cin >> a >> len[f];
		while(cin >> t && t != 0)
			edge[t].push_back(f);//如果f是t的准备工作,那么存在弧<t, f> 
	}
	for(int u = 1; u <= n; ++u)
		for(int v : edge[u])//找所有u的准备工作
			start[v] = max(start[v], start[u]+len[u]);
	for(int v = 1; v <= n; ++v)
		mxFin = max(mxFin, start[v]+len[v]);
	cout << mxFin;
	return 0;
}

http://www.niftyadmin.cn/n/173727.html

相关文章

【蓝桥杯C++】3月21日刷题集训ABC-附百分代码,一目了然

目录 刷题集训 A Day 1 成绩分析 Day 1 饮料换购 刷题集训 B Day 1 分巧克力 Day 1 递增三元组 Day 1 小明的衣服 刷题集训 C Day 1 数字三角形 Day 1 跳跃 Day 1 蓝太子序列 刷题集训 A Day 1 成绩分析 题目描述 小蓝给学生…

正则表达式简介

文章目录一、正则表达式简介总结一、正则表达式简介 正则表达式 正则表达式(Regular Expression)是一种文本模式&#xff0c;包括普通字符&#xff08;例如&#xff0c;a 到 z 之间的字母&#xff09;和特殊字符&#xff08;称为"元字符"&#xff09;。 正则表达式…

【Nacos】Nacos原理详解(注册中心,配置中心)

文章目录一、背景二、CAP理论三、什么是NacosNacos 服务注册需要具备的能力&#xff1a;Nacos的实现原理&#xff1a;四、Nacos原理Nacos 服务注册与订阅的完整流程服务领域模型五、注册中心原理六、配置中心原理七、Nacos 的关键特性包括:八、 面试分析一、背景 服务注册中心…

Electron开发的应用利用github维护版本并自动升级

技术选型&#xff1a; 1、electron&#xff1a;21.3.3 2、electron-vite&#xff1a;1.0.17 3、vue&#xff1a;3.2.45 4、element-plus&#xff1a;2.2.32 背景&#xff1a; 我们写了软件&#xff0c;不可避免的需要进行更新升级&#xff0c;现在流行的是软件自动更新升…

【HTML---各种常用标签,表格table,表单form】

一、表格Table (以下表格中没加<>的&#xff0c;是写在<table>里面的&#xff0c;加<>的写在两个开始和结尾标签内部。 eg:<table border80>) Table属性属性名说明cellpandding填充,单元格中字体的左边留出来相应的空cellspacing单元格与单元格之间的…

Web前端学习:章四 -- JavaScript初级(二)-- DOM

117&#xff1a;DOM-获取父级元素-parentNode 父级&#xff1a;ParentNode 子级&#xff1a;Children 1、一个子级只有一个上一级&#xff0c;即只有一个父级 118&#xff1a;DOM-获取子级元素-children 子级&#xff1a;Children 1、一个父级可以有多个子级 查看子级&…

Tornado 异步协程coroutine原理

协程定义: 协程,又称微线程,纤程。英文名Coroutine。 子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。 所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。 子程序调用…

std::enable_shared_from_this作用:判断异步回调宿主生命周期

std::enable_shared_from_this作用 应用场景&#xff1a;解决异步回调发生时宿主对象销毁的问题&#xff0c;C提供了lambda表达式和各种异步函数&#xff0c;std::thread,std::async或者其他框架api都提供了异步回调方法。api::get(url,> { //解析response}) 回调回来的时候…