山东大学程序设计第八周作业

第一题

区间选点 II

给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点
使用差分约束系统的解法解决这道题

Input

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

Output

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

Example
  • input
    5
    3 7 3
    8 10 3
    6 8 1
    1 3 1
    10 11 1
  • output
    6

解题思路

该题用差分约束求解要考虑构造不等式组,sum[i] 表示数轴上 [0, i] 之间选点的个数,那么对于第 i 个区间 [ai,bi] 需要满足:
(1)sum[bi]-sum[ai-1]>=ci
而且第i个点选择为0,不选为1,所以有:
(2)0<=sum[i]-sum[i-1]<=1
因为要求差分约束系统的最小解,所以需要转化为 ≥ 不等式组从min{ai}来跑SPFA最长路
答案为dis[max(bi)+1]

代码实现

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

#define lmt -1e9

struct Edge {
	int to, next, weight;
}edge[1000010];

int head[50010], num;
int n,a,b,c,MIN,MAX,dis[50010];
bool vis[50010];
queue<int> que;

void add(int x, int y, int w) {
	edge[num].to = y;
	edge[num].weight = w;
	edge[num].next = head[x];
	head[x] = num;
	num++;
}
int get_m(int o,int a,int b)
{
    if(o)
    {
        int mx = max(a, b);
        return max(mx, MAX);
    }
    else
    {
        int mn = min(a, b);
        return min(MIN, mn);
    }

}
void SPFA() {
	while (!que.empty()) que.pop();
	for (int i = MIN; i <= MAX; i++)
        dis[i] = lmt, vis[i] = 0;
    dis[MIN-1] = 0, vis[MIN-1] = 1;
	que.push(MIN-1);
	while (!que.empty())
    {
		int u = que.front();
		que.pop();
		vis[u] = 0;
		for (int i = head[u]; i != -1; i = edge[i].next)
        {
			int v = edge[i].to, w = edge[i].weight;
			if (dis[v] <dis[u] + w) {
				dis[v] = dis[u] + w;
				if (!vis[v]) {
					que.push(v);
					vis[v] = 1;
				}
		}
	}
}
}
int main(){
	cin.sync_with_stdio(false);
	cin >> n;
	MIN = n;
	memset(head, -1, sizeof(head));
	for (int i = 0; i < n; i++){
		cin >> a >> b >> c;
		add(a, b + 1, c);
        MAX = get_m(1,a,b);
		MIN = get_m(0,a,b);

	}
    MAX++;
	MIN++;
	for (int i = MIN; i <= MAX; i++){
		add(i-1, i , 0);
        add(i , i-1, -1);
	}
	SPFA();
	cout << dis[MAX] << endl;
	return 0;
}

第二题

猫猫向前冲

众所周知, 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

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

Example
  • input
    4 3
    1 2
    2 3
    4 3
  • output
    1 2 4 3

解题思路

典型的拓扑排序问题,每次找到入度为0的点,拿出,然后将该点到达的点的入度减一,继续该过程即可。

#include<iostream>
#include<cstring>
using namespace std;

int a[510][510], in[510];
int n, m,cnt;

int main() {
	while (cin >> n >> m) {
		memset(a, 0, sizeof(a));
		memset(in, 0, sizeof(in));
		cnt=0;
		for (int i = 0; i < m; i++) {
			int a1, a2;
			cin >> a1 >> a2;
			if (a[a1][a2] == 0) {
				a[a1][a2] = 1;
				in[a2]++;
			}
		}
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++) {
				if (in[j] == 0) {
					in[j]--;
					cout << j;
					cnt++;
					if (cnt != n) cout << " ";
					else
						cout << endl;
					for (int k = 1; k <= n; k++)
						if (a[j][k] == 1)
							in[k]--;
				break;
			}

		}
	}
	return 0;
}

第三题

班长竞选

大学班级选班长,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开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!

Example
  • input
    2
    4 3
    3 2
    2 0
    2 1
    3 3
    1 0
    2 1
    0 2
  • output
    Case 1: 2
    0 1
    Case 2: 2
    0 1 2

解题思路

一个人得票即其入度,考虑到可能存在的强连通分量,我们要用到kosaraju算法,然后缩点成为无环图。
那么入度我们可以人为的分为两部分:当前SCC内的点的个数-1,还有其他可达该点的SCC中的点,最后我们发现答案一定在出度为0的SCC中,我们找到其中的最大值即可。

代码实现

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
int back_index,scc_num,cnt,n,m;
vector<int>G1[5010],G2[5010],G3[5010];
int SCC[5010],dfn[5010],scc_index[5010],in[5010],ans[5010];
bool vis[5010];

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[back_index++]=x;//后序序列
}
void dfs2(int x)//反图
{
	scc_index[x]=scc_num;
	SCC[scc_num]++;//被标记(同一个强连通标记成scc_num从1开始
	for(int i=0;i<G2[x].size();i++)
		if(!scc_index[G2[x][i]])
			dfs2(G2[x][i]);
}
void dfs3(int x)
{
	vis[x]=1;
	cnt+=SCC[x];
     for(int i=0;i<G3[x].size();i++)
     	if(!vis[G3[x][i]])
			 dfs3(G3[x][i]);
}

int main()
{
	ios::sync_with_stdio(false);
	int N,num=1;
	cin>>N;
	for (int l = 0;l<N;l++)
	{
		cin >> n >> m;
		memset(in,0,sizeof(in));
		for(int i=0;i<n;i++)
        {
            G1[i].clear(); G2[i].clear(); G3[i].clear();
        }
		for(int i=1;i<=m;i++)
		{
			int a,b;
			cin >> a >> b;
			G1[a].push_back(b);
			G2[b].push_back(a);//反图
		}
		memset(vis,0,sizeof(vis));//求强连通分量
		memset(scc_index,0,sizeof(scc_index));
		memset(SCC,0,sizeof(SCC));
		memset(dfn,0,sizeof(dfn));
		back_index=scc_num=0;
		for(int i=0;i<n;i++)//原图
			if(!vis[i])
				dfs1(i);
		for(int i=n-1;i>=0;i--)//反图
		if(!scc_index[dfn[i]])
			scc_num++,dfs2(dfn[i]);
		//scc_index[i]表示i所在的SCC编号
		for(int i=0;i<n;i++)//缩点
        {
	    	for(int j=0;j<G2[i].size();j++)
	       {
			   int idx = scc_index[i], t = G2[i][j];
			   if (idx != scc_index[t])
			   G3[idx].push_back(scc_index[t]); //再反图缩点 //缩点后点变为1-scc_num
	       }
        }
        for(int i=1;i<=scc_num;i++)//记录入度
        	for(int j=0;j<G3[i].size();j++)
        		in[G3[i][j]]++;
		memset(ans,0,sizeof(ans));
        int mx=0;
        for(int i=1;i<=scc_num;i++)//用ans来记录每个点可以到达的点数
        if(!in[i])//入度为0的点
        {
			 memset(vis,0,sizeof(vis));
        	 cnt=-1;
			 dfs3(i);//count就是一个SCC中的点的可以到达的点
			 mx=max(mx,cnt);//mx就是最高票数
			 for(int j=0;j<n;j++)
			 	if(scc_index[j]==i)
				 	ans[j]=cnt;
		}
		cout<<"Case"<<' '<<num<<": "<<mx<<endl;
		bool flag=0;
		   for(int i=0;i<n;i++)
		   {
		   		if(ans[i]==mx)
		   		{
		   			if(!flag)cout<<i;
		   			else cout<<" "<<i;
					flag=1;
			   }
		   }
		cout<<endl;
		num++;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 山东大学计算机科学与技术专业的程序设计期末考试通常会涉及到一系列与编程相关的题目,旨在测试学生的编程思维和动手能力。考试内容一般覆盖了程序设计的基础知识和常见算法的应用。 首先,考试通常会要求学生编写一些基础的程序。这些题目可能涉及到输入输出、基本数据类型和操作、条件判断、循环等。通过这些题目,考官可以了解学生的编码能力和对基本语法的掌握程度。 其次,考试可能会包括一些常见算法的应用题。例如,搜索算法、排序算法、递归算法等。学生需要根据题目的要求,选用合适的算法来解决问题,并写出相应的代码。这部分题目主要考察学生对算法的理解和应用能力。 此外,考试还可能涉及一些与数据结构相关的题目。比如,链表、树、图等数据结构的应用和操作。学生需要了解不同数据结构的特点和使用方式,并能够通过编程实现相应的数据结构和操作。 最后,在程序设计考试中,还可能会出现一些开放性题目。这类题目一般不会给出具体的代码要求,而是提供一个问题或任务,要求学生自己设计和实现相应的程序。这种类型的题目要求学生具备独立思考和创新能力。 总的来说,山东大学程序设计期末考试主要考察学生的编程基础知识、算法应用能力和问题解决能力。通过考试,学生可以巩固和提升自己的编程技能,为未来的学习和工作做好准备。 ### 回答2: 山东大学程序设计期末考试通常是在计算机科学与技术专业的课程中进行的。考试通常采用纸质试卷形式,在规定的时间内完成。考试的内容主要是关于程序设计的基础知识和技巧,以及一定难度的编程题目。 考试内容可能包括但不限于以下几个方面: 1.基础知识:包括计算机基础概念、数据类型、运算符、控制语句等。 2.函数和模块化设计:要求学生掌握如何定义函数,并能够使用函数实现模块化的程序设计。 3.数据结构和算法:考察学生对于常见数据结构(如数组、链表、栈、队列等)的理解和使用,以及对一些基本算法(如排序、查找等)的掌握。 4.编程题目:考察学生对于程序设计的实际应用能力,通常包括一定难度的编程题目。学生需要设计并实现一个满足特定要求的程序。 尽管题目的具体内容可能会有所不同,但考试的目的是为了测试学生对于程序设计的掌握程度以及实际应用能力。因此,考试的题目通常会注重学生对于程序设计思路和解决问题能力的考察。 对于备考,学生可以通过复习课堂笔记、参考教材和做一些练习题来提高自己的编程能力。同时也可以加入一些编程技术交流的平台(如CSDN),与其他同学、老师以及技术专家们交流学习,提升自己的编程水平。 在考试中,学生应该注意仔细阅读题目要求,理清思路,设计好算法,并注意代码的规范与正确性。完成考试后,应该仔细检查代码的功能和效果,确保没有错误和遗漏。 总之,山东大学程序设计期末考试是一个测试学生对于程序设计的掌握程度和实际应用能力的重要考试,通过合理的备考和努力的实践,在考试中获得优异的成绩是可以实现的。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值