Program week8 work

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

思路

可以用贪心解决,这里要求使用差分约束
这里将区间 [0, i] 之间选点的个数对应到求取最短路上的dis[]数组
对于第 i 个区间 [xi,yi] 需要满足 dis[yi]-dis[xi-1]>=ci
同时,需要保证dis是有意义的:0≤dis[i]-dis[i-1]≤1
求该差分约束系统的最小解,转化为 ≥ 不等式组,然后spfa跑最长路,最后答案为dis[max{yi}]

Answer

具体思路见注释

#include<iostream>
#include<queue>
#include<string.h> 
using namespace std;
const int maxn = 1e6 + 100;
const int inf = 1e8;
int a[maxn], b[maxn], c[maxn], s[maxn], dis[maxn];
bool vis[maxn] = { 0 };
queue<int> q;
int head[maxn], tot;
int bmin = inf, bmax = 0;
struct edge {
	int to, next, w;
}e[maxn];
void add(int u, int v, int w) {
	e[++tot].to = v, e[tot].w = w, e[tot].next = head[u], head[u] = tot;
}
void spfa(int s) {
	dis[s] = 0;
	vis[s] = 1;
	q.push(s);
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		for (int i = head[u]; i; i = e[i].next) {
			int v = e[i].to;
			if (dis[v] < dis[u] + e[i].w) {
				dis[v] = dis[u] + e[i].w;
				if (!vis[v]) {
					q.push(v);
					vis[v] = 1;
				}
			}
		}
	}
}
int main() {
	int n, x, y, z;
	cin >> n;
	memset(dis,128,sizeof(dis));//将dis数组初始化为int的最小值 
	for (int i = 1; i <= n; i++) {
		cin >> x >> y >> z;
		add(x , y+1, z);
		bmin = min(bmin, x);
		bmax = max(bmax, y+1);
	}
	for (int i = bmin; i <= bmax; i++)
	{
		add(i - 1, i, 0);
		add(i, i - 1, -1);
	}
	spfa(bmin-1);
	cout << dis[bmax];
	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

思路

拓扑排序的例题,将入度为0的点加入队列
然后在遍历过程中取队首元素,若删掉该边后队首元素的邻接点的入度变成0,则将该邻接点压入队列
因为需要以字典序输出,所以用优先队列代替队列

Answer

具体思路见注释

#include<iostream>
#include<queue>
#include<string.h>
#include<functional>
using namespace std;
const int maxn =1e6+100;
int n,m,indegree[maxn]={0};	
vector<int> ans;
int head[maxn],tot;
struct edge{
	int to,next;
}e[maxn];
void add(int u,int v){
	e[++tot].to=v,e[tot].next=head[u],head[u]=tot;
}
void init(){
	memset(head,0,sizeof(head));
	tot=0;
	while(ans.size())ans.pop_back();
}
bool toposort(){
	priority_queue<int,vector<int>,greater<int> >q;
	for(int i=1;i<=n;i++){
		if(indegree[i]==0)q.push(i);
	}

	while(!q.empty()){
		int u=q.top();
		ans.push_back(u);
		q.pop();
		for(int i=head[u];i;i=e[i].next){
			int v=e[i].to;
			if(--indegree[v]==0)q.push(v);
		}
	}
	if(ans.size()==n)return 1;
	else return 0;
}
int main(){
	while(cin>>n>>m){
		init();
		int x,y;
		for(int i=1;i<=m;i++){
			cin>>x>>y;
			add(x,y);
			indegree[y]++;
		}
		if(toposort()){
			cout<<ans[0];
			for(int i=1;i<n;i++){
				cout<<" "<<ans[i];
			}
		}
		cout<<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算法求出强连通分量,然后缩点(把位于同一个scc的点合并起来,把每个scc看做一个点);dfs1求出逆后序序列,dfs2根据逆后序序列找出所有强连通分量。
思考后发现最后答案一定位于在一开始出度为 0 的 SCC 中。因此遍历一开始出度为0的点,统计其所能到达的所有的点。

关于逆后序序列为什么能求scc的想法:
逆后序序列使得不同起始点的子图相互隔离,同一起始点的子图中的各个scc能够按顺序遍历,因此保证可以正确搜索得到scc

Answer

具体思路见注释

#include<iostream>
#include<queue>
#include<vector>
#include<string.h>
const int N = 5010;
using namespace std;
struct edge
{
	int v, next;
}e[30030], e2[30030], e3[30030];

int head[N], head2[N], head3[N], dfn[N], c[N], vis[N],scc[N], ans[N], indegree[N];
int dcnt, label,tot, tot2, tot3;
int T,n, m, x;
bool flag;
void add(int u, int v)
{
	e[tot].v = v;
	e[tot].next = head[u];
	head[u] = tot;
	tot++;
}
void add2(int u, int v)
{
	e2[tot2].v = v;
	e2[tot2].next = head2[u];
	head2[u] = tot2;
	tot2++;
}
void add3(int u, int v)
{
	e3[tot3].v = v;
	e3[tot3].next = head3[u];
	head3[u] = tot3;
	tot3++;
}
void init() {
	memset(head, -1, sizeof(head));
	memset(head2, -1, sizeof(head2));
	memset(head3, -1, sizeof(head3));
	memset(indegree, 0, sizeof(indegree));
	memset(ans, 0, sizeof(ans));
	memset(c, 0, sizeof(c));
	memset(vis, 0, sizeof(vis));
	memset(scc, 0, sizeof(scc));
	tot3 = 0,tot2 = 0,tot = 0,dcnt = label = 0;
}
void dfs1(int s)
{
	vis[s] = 1;
	for (int i = head[s]; i != -1; i = e[i].next)
	{
		int v = e[i].v;
		if (vis[v] == 0)
		{
			dfs1(v);
		}
	}
	dfn[++dcnt] = s;
}
void dfs2(int s)
{
	c[s] = label;
	scc[label]++;
	for (int i = head2[s]; i != -1; i = e2[i].next)
	{
		int v = e2[i].v;
		if (c[v] == 0)
			dfs2(v);
	}
}

void kosaraju()
{

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

}

void dfs3(int s)
{
	vis[s] = 1;
	x += scc[s];
	for (int i = head3[s]; i != -1; i = e3[i].next)
	{
		if (!vis[e3[i].v])
			dfs3(e3[i].v);
	}
}


int main()
{
	cin >> T;
	for (int i = 1; i <= T; i++)
	{
		cin >> n >> m;
		init();
		for(int i=1;i<=m;i++)
		{
			int u, v;
			cin>>u>>v;
			add(u+1, v+1);
			add2(v+1, u+1);
		}
		kosaraju();
		//缩点
		for (int j = 1; j <= n; j++)
		{
			for (int k = head[j]; k != -1; k = e[k].next)
			{
				if (c[j] != c[e[k].v])
				{
					add3(c[e[k].v], c[j]);
					indegree[c[j]]++;
				}
			}
		}
		int max = 0;
		for (int j = 1; j <= label; j++)
		{
			if (indegree[j] == 0)
			{
				x = 0;
				memset(vis, 0, sizeof(vis));
				dfs3(j);
				ans[j] = x;
				if (x > max)
					max = x;
			}
		}
		cout << "Case " << i << ":" << max-1 << endl;
		flag = 0;
		for (int j = 1; j <= n; j++)
		{
			if (ans[c[j]] == max)
			{
				if (flag)flag = 1, cout << j-1;
				else cout << " " << j-1;
			}
		}
		cout<<endl;
	}
}
************************第一版代码************************
#include<iostream>
#include<string.h>
#include<queue>
using namespace std;
const int maxn = 1e6 + 100;
int T, n, m, x, y, dcnt = 0, label = 0, ans = 0;
int scc[maxn], vis[maxn], dfn[maxn], indegree[maxn] = { 0 }, num[maxn] = { 0 };
int head1[maxn], head2[maxn], head[maxn], head0[maxn], tot1, tot2, tot, tot0;
struct edge {
	int to, next, w;//w为1代表存在边,为0代表不存在
}e1[maxn], e2[maxn], e[maxn], e0[maxn];
void add1(int u, int v) {
	e1[++tot1].to = v, e1[tot1].next = head1[u], head1[u] = tot1;
}
void add2(int u, int v) {
	e2[++tot2].to = v, e2[tot2].next = head2[u], head2[u] = tot2;
}
void add(int u, int v, int w) {
	e[++tot].to = v, e[tot].w = w, e[tot].next = head[u], head[u] = tot;
}
void add0(int u, int v) {
	e0[++tot0].to = v, e0[tot0].next = head0[u], head0[u] = tot0;
}
void init() {
	memset(head, 0, sizeof(head));
	memset(head1, 0, sizeof(head1));
	memset(head2, 0, sizeof(head2));
	memset(head0, 0, sizeof(head0));
	memset(dfn, 0, sizeof(dfn));
	memset(indegree, 0, sizeof(indegree));
	memset(num, 0, sizeof(num));
	dcnt = 0, label = 0, ans = 0, tot1=0, tot2=0, tot=0, tot0 = 0;
}
void dfs1(int x) {
	vis[x] = 1;
	for (int i = head1[x]; i; i = e1[i].next) {
		int v = e1[i].to;
		if (!vis[v])dfs1(v);
	}
	dfn[++dcnt] = x;
}
void dfs2(int x) {
	scc[x] = label;
	num[label]++;
	for (int i = head2[x]; i; i = e2[i].next) {
		int v = e2[i].to;
		if (!scc[v])dfs2(v);
	}
}
int xx=0;
void dfs3(int x) {
	vis[x] = 1;
	xx+=num[x];
	for (int j = head0[x]; j; j = e0[j].next) {
		int v = e0[j].to;
		if(!vis[v])
		dfs3(v);
	}
}
void kosaraju() {
	memset(scc, 0, sizeof scc);
	memset(vis, 0, sizeof vis);
	for (int i = 0; i < n; i++)
		if (!vis[i])dfs1(i);
	for (int i = n-1; i >=0 ; i--)
		if (!scc[dfn[i]]) label++, dfs2(dfn[i]);
}
void suodian() {
	for (int i = 0; i < n; i++)
		for (int j = head1[i]; j; j = e1[j].next) {
			int v = e1[j].to;
			//注意这里把i和v反过来,这样保存的就是反向边,便于后面拓扑寻找前序节点
			if (scc[i] != scc[v]) {
				add(scc[i], scc[v], 1);
				add0(scc[v], scc[i]);
				indegree[scc[i]]++;
			}
		}
}
int main() {
	cin >> T;
	for (int i = 1; i <= T; i++) {
		init();
		cin >> n >> m;
		for (int j = 1; j <= m; j++) {
			cin >> x >> y;
			add1(x, y);
			add2(y, x);
		}
		kosaraju();
		suodian();
		for (int j = 1; j <= label; j++){
			if (indegree[j] == 0){
				memset(vis, 0, sizeof(vis));
				dfs3(j);
				num[j] = xx;
			}
		}
		int target;
		for (int j = 1; j <= label; j++) {
			if (ans < num[j]) {
				ans = num[j];
				target = j;
			}
		}
		ans--;
		cout << "Case " << i << ":" << ans << endl;
		int flag = 0;
		for (int k = 0; k < n; k++) {
			if (scc[k] == target) {
				if (!flag)flag = 1, cout << k;
				else cout << " " << k;
			}
		}
		cout << endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值