程序设计思维与实践 Week8 作业

A - 区间选点 II

题目描述

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

使用差分约束系统的解法解决这道题
使用差分约束系统的解法解决这道题
使用差分约束系统的解法解决这道题
使用差分约束系统的解法解决这道题
使用差分约束系统的解法解决这道题
  1. 输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每一行两个用空格隔开的整数 a,b 表示区间的左右端点。1 <= n <= 50000, 0 <= ai <= bi <= 50000 并且 1 <= ci <= bi - ai+1。
  2. 输出一个整数表示最少选取的点的个数
    -input:
    5
    3 7 3
    8 10 3
    6 8 1
    1 3 1
    10 11 1
  3. output:
    6

题目分析

最近学的差分约束,这道题也要求使用差分约束做(我也想知道还有啥其他方式做),那么我们要求最小值就是构造大于号以及求最大路了

  1. 首先我们构造条件设计数组sum[i]代表从0到i所选点的个数,则根据题意得,sum[bi]-sum[ai-1]>=ci,是一个条件,还有我们要保证每个点只能是选一个或者不选,所以还有1>=sum[k]-sum[k-1]>=0,这样构造好了之后,就可以使用spfa进行最长路求解了
  2. 注意点:A和D点得差异应该是对于第一个点,如果使用上述的坐标系,我们无法保证第一个点的约束,因为不存在1>=sum[0]-sum[-1]>=0,所以我们把上述的坐标轴向右移动一格,

代码

#include<iostream>
#include<queue>
using namespace std;
const int nmax=5E5+10;
const int inf=1E9;
int dis[nmax],inq[nmax],head[nmax],cnt[nmax];
int n,m,k,tmp,tot=0;
struct edge{
	int to,next,w;
}edges[nmax];
void add(int x,int y,int z){
	edges[++tot].to=y,edges[tot].w=z;
	edges[tot].next=head[x],head[x]=tot;
}
void init(){
	fill(dis,dis+nmax,-inf);
	fill(inq,inq+nmax,0);
	//fill(cnt,cnt+nmax,1);
}
void spfa(int st){//最长路
	init();
	queue<int> que;
	dis[st]=0;inq[st]=1;
	que.push(st);
	while(!que.empty()){
		int x=que.front();que.pop();inq[x]=0;
		for(int i=head[x];i;i=edges[i].next){
			int y=edges[i].to,w=edges[i].w;
			if(dis[y]<dis[x]+w){
				dis[y]=dis[x]+w;
				cnt[y]=cnt[x]+1;
				//if(cnt[y]>=n) 
				if(!inq[y]) {que.push(y);inq[y]=1;}
			}
		}
	}
}

int main(){
	init();
	scanf("%d",&n);
	int a,b,c,end=-1;
	for(int i=0;i<n;i++){
		scanf("%d%d%d",&a,&b,&c);
		if(b+1>end) end=b+1;
		add(a,b+1,c);
	}
	for(int i=1;i<=end;i++){
		add(i-1,i,0);
		add(i,i-1,-1);
	}
	spfa(0);
	printf("%d",dis[end]);
	return 0;
}

B - 猫猫向前冲

题目描述

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

  • 输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示猫猫的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即编号为 P1 的猫猫赢了编号为 P2 的猫猫。
  • 给出一个符合要求的排名。输出时猫猫的编号之间有空格,最后一名后面没有空格!

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

4 3
1 2
2 3
4 3

Sample Output

1 2 4 3

题目分析

这就是一个拓扑排序的题目,每个输入就是一个优先边,使用上课讲的kahn算法,一个像bfs一样的算法就能解决.首先遍历所有的边,将入度为0的放到最小堆中,之后每次取出堆顶,并且遍历他的所有的为出度的边,将每个到的点的入减1,相当于删去了这条边,并检查此时到的点的入度,为0则放入堆,直到堆为空,最小堆保证了最小的序列

代码

#include<iostream>
#include<queue>
#include<vector>
using namespace std;
const int nmax=5E2+10;
const int inf=1E9;
int head[nmax],cnt[nmax];
int n,m,k,tmp,tot=0;
vector<int> res;
struct edge{
	int to,next,w;
}edges[nmax];
void add(int x,int y,int z){
	edges[++tot].to=y,edges[tot].w=z;
	edges[tot].next=head[x],head[x]=tot;
}
void init(){
	//fill(dis,dis+nmax,-inf);
	//fill(inq,inq+nmax,0);
	fill(cnt,cnt+nmax,0);
	fill(head,head+nmax,0);
	tot=0;
	res.clear();
}
void kahn(){
	priority_queue<int> heap;
	for(int i=1;i<=n;i++){if(cnt[i]==0) heap.push(-i);}
	while(!heap.empty()){
		int x=-heap.top();heap.pop();
		res.push_back(x);
		for(int i=head[x];i;i=edges[i].next){
			int y=edges[i].to;
			--cnt[y];
			if(!cnt[y]) heap.push(-y);
		}
	}
}
int main(){
	while(~scanf("%d%d",&n,&m)){
		init();
		int a,b;
		for(int i=0;i<m;++i){
			scanf("%d%d",&a,&b);
			++cnt[b];
			add(a,b,1);
		}
		kahn();
		for(auto iter=res.begin();iter!=res.end();++iter){
			printf(iter==res.begin()?"%d":" %d",(*iter));
		}
		printf("\n");
	}
	return 0;
}

C - 班长竞选

题目描述

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

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

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

  3. Sample Input
    2
    4 3
    3 2
    2 0
    2 1

    3 3
    1 0
    2 1
    0 2

  4. Sample Output
    Case 1: 2
    0 1
    Case 2: 2
    0 1 2

题目分析

这道题就是一个先求出所有的强连通分量,后将分量缩点,后遍历的题目

1.首先使用kosaraju算法,这个算法可以通过逆后序序列求出所有的强连通分量
求得逆后续序列于vetcor里:

void dfs(int st){
	if(vis[st]) return ;
	vis[st]=1;
	for(int i=head[st];i;i=edges[i].next){
		dfs(edges[i].to);
	}
	res.push_back(st);
}

使用kosaraju,遍历逆后序序列,使用dfs每次遍历到的都是同一个强连通分量,遍历后标记在c数组中
调用:

for(auto iter=res.begin();iter!=res.end();++iter){
		int key=*iter;
		if(!c[key]) {scnt++;cc[scnt]=1;dfs_scc(key);} 
	}

遍历

void dfs_scc(int st){
	c[st]=scnt;
	for(int i=head_revr[st];i;i=revr[i].next){
		int y=revr[i].to;
		if(!c[y]) {dfs_scc(y);++cc[scnt];}
	}
}

这样后我们就可以得到一个标记好1-n的分量了,我们再开一个图,进行缩点

for(int i=0;i<n;++i){
		for(int j=head[i];j;j=edges[j].next){
			if(c[i]!=c[edges[j].to]){
				st.insert(make_pair(c[edges[j].to],c[i]));
			}
		}
	}
	for(auto it=st.begin();it!=st.end();it++){
		add(it->first,it->second,3);
		//printf("%d->%d\n",it->first,it->second);
	}

遍历所有的点,如果不是同一个连通分量就建立边,用set防止重复的边,并且标记出读,因为最后的结果最多的点一定是出度为零的
之后遍历所有出度为0的点,计算出他们的票数

代码

#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;
const int nmax=5E4+10;
const int inf=1E9;
int head[nmax],vis[nmax],head_revr[nmax],c[nmax],cc[nmax],dis[nmax],ak[nmax];
int myhead[nmax];
int n,m,tot1=0,tot2=0,myround,scnt=0,mytot=0;
vector<int> res;
set<pair<int,int> > st;
struct edge{
	int to,next,w;
}edges[nmax],revr[nmax],myedge[nmax],*ped;

void add(int x,int y,int z){
	int *hh,*tot;
	if(z==1){ped=edges,hh=head,tot=&tot1;}
	else if(z==2){ped=revr,hh=head_revr,tot=&tot2;}
	else {ped=myedge,hh=myhead,tot=&mytot;dis[y]++;}
	(*tot)++;
	ped[*tot].to=y;
	ped[*tot].next=hh[x],hh[x]=*tot;
}
void init(){
	st.clear();
	res.clear();
	fill(ak,ak+n+1,0);
	fill(myhead,myhead+n+1,0);
	fill(head,head+n+1,0);
	fill(vis,vis+n+1,0);
	fill(head_revr,head_revr+n+1,0);
	fill(c,c+n+1,0);
	fill(cc,cc+n+1,0);
	fill(dis,dis+n+1,0);
	tot1=1,tot2=1,scnt=0,mytot=0;
}
void dfs(int st){
	if(vis[st]) return ;
	vis[st]=1;
	for(int i=head[st];i;i=edges[i].next){
		dfs(edges[i].to);
	}
	res.push_back(st);
}
void dfs_scc(int st){
	c[st]=scnt;
	for(int i=head_revr[st];i;i=revr[i].next){
		int y=revr[i].to;
		if(!c[y]) {dfs_scc(y);++cc[scnt];}
	}
}
int dfs_get(int st){
	if(vis[st]) return 0;
	int ans=0;
	vis[st]=1;
	for(int i=myhead[st];i;i=myedge[i].next){
		ans+=dfs_get(myedge[i].to);
	}
	return ans+cc[st];
}
void kosaraju(){
	for(int i=0;i<n;++i) dfs(i);
	reverse(res.begin(),res.end());//得到后逆序列
	for(auto iter=res.begin();iter!=res.end();++iter){
		int key=*iter;
		if(!c[key]) {scnt++;cc[scnt]=1;dfs_scc(key);} 
	}
	//for(int i=1;i<=scnt;i++) printf("dis[%d]=%d\n",i,dis[i]);
	for(int i=0;i<n;++i){
		for(int j=head[i];j;j=edges[j].next){
			if(c[i]!=c[edges[j].to]){
				st.insert(make_pair(c[edges[j].to],c[i]));
			}
		}
	}
	for(auto it=st.begin();it!=st.end();it++){
		add(it->first,it->second,3);
		//printf("%d->%d\n",it->first,it->second);
	}
	int mymax=-1;
	for(int i=1;i<=scnt;i++){
		if(!dis[i]) { fill(vis,vis+n+1,0);ak[i]=dfs_get(i);mymax=max(mymax,ak[i]);}
	}
	printf("%d\n",mymax-1);
	bool flag=true;
	for(int i=0;i<n;++i){
		int tmp_scnt=c[i];
		if(ak[tmp_scnt]==mymax) {
			if(flag){
				printf("%d",i);
				flag=false;
			}
			else printf(" %d",i);
		}
	}
}
int main(){
	int a,b;
	scanf("%d",&myround);
	for(int kp=1;kp<=myround;++kp){
		scanf("%d%d",&n,&m);
		init();
		for(int i=0;i<m;++i){
			scanf("%d%d",&a,&b);
			add(a,b,1);
			add(b,a,2);
		}
		printf("Case %d: ",kp);
		kosaraju();
		printf("\n");
	}
	return 0;
}

ps:其实我一开始写的是另一个,在做强连通分量的时候缩点,感觉应该可以,但是没过,求指教!代码如下:

#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
using namespace std;
const int nmax=5E4+10;
const int inf=1E9;
int head[nmax],vis[nmax],head_revr[nmax],c[nmax],cc[nmax],dis[nmax];//cc[i]是每个强连通分量的中含有点的个数,dis是指向他的最大票数的强连通分量的的票数
int n,m,tot1=0,tot2=0,myround,scnt=0;
vector<int> res;
struct edge{
	int to,next,w;
}edges[nmax],revr[nmax],*ped;

void add(int x,int y,int z){
	int *hh,*tot;
	if(z==1){ped=edges,hh=head,tot=&tot1;}
	else {ped=revr,hh=head_revr,tot=&tot2;}
	(*tot)++;
	ped[*tot].to=y;
	ped[*tot].next=hh[x],hh[x]=*tot;
}
void init(){
	res.clear();
	fill(head,head+n+1,0);
	fill(vis,vis+n+1,0);
	fill(head_revr,head_revr+n+1,0);
	fill(c,c+n+1,0);
	fill(cc,cc+n+1,0);
	fill(dis,dis+n+1,0);
	tot1=1,tot2=1,scnt=0;
}
void dfs(int st){
	if(vis[st]) return ;
	vis[st]=1;
	for(int i=head[st];i;i=edges[i].next){
		dfs(edges[i].to);
	}
	res.push_back(st);
}
void dfs_scc(int st){
	c[st]=scnt;
	for(int i=head_revr[st];i;i=revr[i].next){
		int y=revr[i].to;
		if(!c[y]) {dfs_scc(y);++cc[scnt];}
		else if(c[y]!=scnt) dis[scnt]=max(dis[scnt],cc[c[y]]+dis[c[y]]);//防止循环吃自己家的
	}
}
void kosaraju(){
	for(int i=0;i<n;++i) dfs(i);
	reverse(res.begin(),res.end());//得到后逆序列
    //for(auto it=res.begin();it!=res.end();it++) printf("%d ",*it);
	for(auto iter=res.begin();iter!=res.end();++iter){
		int key=*iter;
		if(!c[key]) {scnt++;cc[scnt]=1;dfs_scc(key);} 
	}
	//for(int i=1;i<=scnt;i++) printf("dis[%d]=%d\n",i,dis[i]);
	int mymax=-1;
	vector<int> ans;
	for(int i=scnt;i>0;--i){
		mymax=max(mymax,dis[i]+cc[])
	}
	printf("%d\n",mymax-1);
	bool flag=true;
	for(int i=0;i<n;++i){
		int tmp_scnt=c[i];
		if(cc[tmp_scnt]+dis[tmp_scnt]==mymax) {
			if(flag){
				printf("%d",i);
				flag=false;
			}
			else printf(" %d",i);
		}
	}
}
int main(){
	int a,b;
	scanf("%d",&myround);
	for(int kp=1;kp<=myround;++kp){
		scanf("%d%d",&n,&m);
		init();
		for(int i=0;i<m;++i){
			scanf("%d%d",&a,&b);
			add(a,b,1);
			add(b,a,2);
		}
		printf("Case %d: ",kp);
		kosaraju();
		printf("\n");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值