Week8 作业

A - 区间选点 II

题目

给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点

使用差分约束系统的解法解决这道题

Input

输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。

Output

输出一个整数表示最少选取的点的个数

Sample Input

5
3 7 3
8 10 3
6 8 1
1 3 1
10 11 1

Sample Output

6

做法

转换为单源最短路问题,利用spfa求解。对于差分约束的每一个不等式约束xi-xj<=ck都可以移项变形为xi<=ck+xj。令ck=w(i,j),dis[i]= xi,dis[j]= xj,那么原式变为 dis[i]≤dis[i]+ w(i,j),与最短路问题中的松弛操作相似

本题具体做法:
构造不等式组
• 记 sum[i] 表示数轴上 [0, i] 之间选点的个数
• 对于第 i 个区间 [ai, bi] 需要满足 sum[bi] − sum[ai− 1] ≥ ci
• 需要保证 sum 是有意义的 0 ≤ sum[i]− sum[i − 1 ]≤ 1
• 求该差分约束系统的最小解,转化为 ≥ 不等式组跑最长路,答案为sum[max{bi}]

需要注意的是,区间从0开始,计算sum[i-1]可能会出错,所以把所有区间都往右移一个单位。

代码

#include<iostream>
#include<queue>
#include<stdio.h>
using namespace std;

struct edge
{
	int v,w;
};
vector<edge> G[50050];
int dis[50050];
int inq[50050];//是否在队列 

int main()
{
	int n;
	scanf("%d",&n);
	int maxx=0; 
	for(int i=0;i<=50009;i++)
	{
		dis[i]=-1;
		inq[i]=0;
	} 
	for(int i=0;i<n;i++)
	{
		int A,B,C;
		scanf("%d%d%d",&A,&B,&C);
		B++;//往右平移一个单位的区间 
		edge e;
		e.v=B;e.w=C;
		G[A].push_back(e);
		if(maxx<B)
		 maxx=B;
	}
	for(int i=0;i<maxx;i++)
	{
		edge e;
		e.v=i+1;
		e.w=0;
		G[i].push_back(e);
		e.v=i;
		e.w=-1;
		G[i+1].push_back(e);
	}
	
		queue<int> q;
		dis[0]=0;
		q.push(0);
		while(!q.empty())
		{
			int t=q.front();
			q.pop();
			inq[t]=0;
			for(int i=0;i<G[t].size();i++)
			{
				int vi=G[t][i].v;
				int wi=G[t][i].w;
				if(dis[vi]<dis[t]+wi)
				{
					dis[vi]=dis[t]+wi;
					if(inq[vi]==0)//点不在队列 
					{
						q.push(vi);
						inq[vi]=1;
					}
				}
			}	
		}	
	cout<<dis[maxx]<<endl;
	return 0;
}

B - 猫猫向前冲

题目

众所周知, TT 是一位重度爱猫人士,他有一只神奇的魔法猫。
有一天,TT 在 B 站上观看猫猫的比赛。一共有 N 只猫猫,编号依次为1,2,3,…,N进行比赛。比赛结束后,Up 主会为所有的猫猫从前到后依次排名并发放爱吃的小鱼干。不幸的是,此时 TT 的电子设备遭到了宇宙射线的降智打击,一下子都连不上网了,自然也看不到最后的颁奖典礼。
不幸中的万幸,TT 的魔法猫将每场比赛的结果都记录了下来,现在他想编程序确定字典序最小的名次序列,请你帮帮他。

Input

输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示猫猫的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即编号为 P1 的猫猫赢了编号为 P2 的猫猫。

Output

给出一个符合要求的排名。输出时猫猫的编号之间有空格,最后一名后面没有空格!

其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。

Sample Input

4 3
1 2
2 3
4 3

Sample Output

1 2 4 3

做法

拓扑排序 kahn算法:
•将入度为 0 的点组成一个集合 S
• 每次从 S 里面取出一个顶点 u (可以随便取)放入 L , 然后遍历顶点 u 的所有边 (u, v) , 并删除之,并判断如果该边的另一个顶点 v,如果在移除这一条边后入度为 0 , 那么就将这个顶点放入集合 S 中。不断地重复取
出顶点然后重复这个过程……
• 最后当集合为空后,就检查图中是否存在任何边。如果有,那么这个图一定有环路,否者返回 L , L 中顺序就是拓扑排序的结果

本题要求字典序最小的拓扑序列。任一时刻队列中所有结点意味着已经可以确定名次关系并且互相之间没有依赖; 取队首的时候可以取队列中编号最小的点出队。因此,可以使用优先级队列替换队列。

需要注意的是优先级队列默认为大根堆,入队时可以用相反数实现小根堆。

代码

#include<iostream>
#include<queue>
#include<vector>
using namespace std;

vector<int> e[600];//记录有向边 
int indeg[600];//每个点入度 
int ans[600];//按序记录结果 

int main()
{
	int n,m;
	while(cin>>n>>m)
	{
		for(int in=0;in<=n;in++)
		{
			e[in].clear();
			indeg[in]=0;
			ans[in]=0;
		}
		for(int im=0;im<m;im++)
		{
			int a,b;
			cin>>a>>b;
			e[a].push_back(b);
			indeg[b]++;
		}
		//Kahn 
		priority_queue<int> q;
		int k=1;//ans下标 
		for(int i=1;i<=n;i++)
		{
			if(indeg[i]==0)
			{
				//cout<<i<<endl;
				q.push(-i);
			}
		}
		while(!q.empty())
		{
			int u=-q.top();
			//cout<<u<<endl;
			q.pop();
			ans[k++]=u;
			for(int i=0;i<e[u].size();i++)
			{
				int t=e[u][i];
				indeg[t]--;
				if(indeg[t]==0)
				{
					q.push(-t);
				}
			}
		}
		for(int i=1;i<n;i++)
		{
			cout<<ans[i]<<' ';
		}
		cout<<ans[n]<<endl;
	}
	return 0;
}

C - 班长竞选

题目

大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?

Input

本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 <m <= 30000),接下来有 M 行包含两个整数 A 和 B(A != B) 表示 A 认为 B 合适。

Output

对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!

Sample Input

2
4 3
3 2
2 0
2 1

3 3
1 0
2 1
0 2

Sample Output

Case 1: 2
0 1
Case 2: 2
0 1 2

做法

Kosaraju找到图中所有的强连通分量。
第一遍dfs确定原图的逆后序序列
第二遍dfs在反图中按照逆后序序列进行遍历,每次由起点遍历到的点构成一个SCC(同一个SCC点i的c[i]值相同)

本题可以先用kosaraju求出所有的SCC并缩点,那么对于属于第i个SCC的点来说,得票是当前SCC中的点数目-1(除去自己)加上可以到达当前SCC的其他SCC的点数目总和。另外,最后答案一定在出度为0的SCC中,这样可以将边反向,对每个入度为0的点进行dfs,计算能到达的点,就是得票数。在这些得票中找到最大值,得票数可能有相同,需要再次遍历得票数找出所有结果,并且将结果升序输出。

缩点后的边可能会有重复,利用sort排序和unique()操作删除重复的边。

代码

#include<iostream>
#include<vector>
#include<string.h>
#include<algorithm>
using namespace std;

vector<int> G1[6000],G2[6000],G3[6000];
int c[6000];//i号点所在scc编号 
int dfn[6000];//dfs后序列中第i个点 
int vis[6000];//
int dcnt,scnt;//计数 
int n,m;
int indeg[6000];//记录出度 
int num[6000];//记录每个scc内点的数量 
int ans[6000];//记录最后票数 
int ans1[6000];//记录最后结果 

void dfs1(int x)
{
	vis[x]=1;
	for(int i=0;i<G1[x].size();i++)
	{
		if(!vis[G1[x][i]])
		dfs1(G1[x][i]);
	}
	dfn[dcnt++]=x;
}

void dfs2(int x)
{
	c[x]=scnt;
	for(int i=0;i<G2[x].size();i++)
	{
		if(!c[G2[x][i]])
		dfs2(G2[x][i]);
	}
}

void kosaraju()
{
	dcnt=scnt=0;
	for(int i=0;i<=n;i++)
	{
		c[i]=0;
		vis[i]=0;
	}
	for(int i=0;i<n;i++)
	{
		if(!vis[i])
		 dfs1(i);
	}
	for(int i=n-1;i>=0;i--)
	{
		if(!c[dfn[i]])
		{
			++scnt;
			dfs2(dfn[i]);
		}
	}
}

void dfs3(int x,int y)
{
	vis[x]=1;
	for(int ii=0;ii<G3[x].size();ii++)
	{
		int t1=G3[x][ii];
		//cout<<vis[t1]<<endl;
		if(!vis[t1])
		{
			ans[y]=ans[y]+num[t1];
			//cout<<ans[y]<<endl;
			dfs3(t1,y);
		}
	}
} 

int main()
{
	int t;
	cin>>t;
	for(int it=1;it<=t;it++)
	{
		cin>>n>>m;
		for(int in=0;in<=n;in++)
		{
			G1[in].clear();
			G2[in].clear();
			G3[in].clear();
			num[in]=0;
			indeg[in]=0;
			ans[in]=0;
			ans1[in]=0;
		} 
		for(int im=0;im<m;im++)
		{
			int a,b;
			cin>>a>>b;
			G1[a].push_back(b);
			G2[b].push_back(a);
		}
		kosaraju();
		//
		for(int i=0;i<n;i++)
		{
			num[c[i]]++;
			//cout<<c[i]<<endl;
		}
		
		for(int i=0;i<n;i++)
		{
			for(int j=0;j<G2[i].size();j++)
			{
				int t=G2[i][j];
				if(c[i]!=c[t])
				{
					G3[c[i]].push_back(c[t]);
					indeg[c[t]]++;
				}
			}
		}
		for(int i=1;i<=scnt;i++)//删除重复元素 
		{
			sort(G3[i].begin(),G3[i].end());
			vector<int>::iterator tt=unique(G3[i].begin(),G3[i].end());
			G3[i].erase(tt,G3[i].end());
		}
		for(int i=1;i<=scnt;i++)
		{
			//cout<<indeg[i]<<endl;
			if(indeg[i]==0)
			{
				for(int k=0;k<=n;k++)
				{
					vis[k]=0;
				}
				dfs3(i,i);
				ans[i]=ans[i]+num[i]-1;
				//cout<<ans[i]<<endl;
			}
		}
		int maxx=0;//找到最大得票
		for(int i=1;i<=scnt;i++)
		{
			if(maxx<ans[i])
			{
				maxx=ans[i];
			}
		}
		cout<<"Case "<<it<<": "<<maxx<<endl;
		
		int indexx=0;
		
		for(int i=1;i<=scnt;i++)
		{
			if(ans[i]==maxx)
			{
				for(int j=0;j<n;j++)
				{
					if(c[j]==i)
					{
						//cout<<" "<<j;
						ans1[indexx++]=j;
					}
				}
			}
		}
		sort(ans1,ans1+indexx);
		for(int i=0;i<indexx-1;i++)
		{
			cout<<ans1[i]<<' ';
		}
		cout<<ans1[indexx-1];
		cout<<endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
水资源是人类社会的宝贵财富,在生活、工农业生产中是不可缺少的。随着世界人口的增长及工农业生产的发展,需水量也在日益增长,水已经变得比以往任何时候都要珍贵。但是,由于人类的生产和生活,导致水体的污染,水质恶化,使有限的水资源更加紧张。长期以来,油类物质(石油类物质和动植物油)一直是水和土壤中的重要污染源。它不仅对人的身体健康带来极大危害,而且使水质恶化,严重破坏水体生态平衡。因此各国都加强了油类物质对水体和土壤的污染的治理。对于水中油含量的检测,我国处于落后阶段,与国际先进水平存在差距,所以难以满足当今技术水平的要求。为了取得具有代表性的正确数据,使分析数据具有与现代测试技术水平相应的准确性和先进性,不断提高分析成果的可比性和应用效果,检测的方法和仪器是非常重要的。只有保证了这两方面才能保证快速和准确地测量出水中油类污染物含量,以达到保护和治理水污染的目的。开展水中油污染检测方法、技术和检测设备的研究,是提高水污染检测的一条重要措施。通过本课题的研究,探索出一套适合我国国情的水质污染现检测技术和检测设备,具有广泛的应用前景和科学研究价值。 本课题针对我国水体的油污染,探索一套检测油污染的可方案和方法,利用非分散红外光度法技术,开发研制具有自主知识产权的适合国情的适于野外便携式的测油仪。利用此仪器,可以检测出被测水样中亚甲基、甲基物质和动植物油脂的污染物含量,为我国众多的环境检测站点监测水体的油污染状况提供依据。
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出题参考。 ### 使用景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值