程设第八周

第一题

简单题意

区间选点(查分约束)

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

要求使用查分约束解决。

Input

输入第一行一个整数 n 表示区间的个数,接下来的 n 行,每行为ai,bi,ci。

Output

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

思路

构建不等式组,设dis[i]表示区间[0,i]要选dis[i]个点,则对于一组输入ai,bi,ci,则有dis[bi]-dis[ai-1]>=ci,此外还有0<=dis[i]-dis[i-1]<=1。

这样我们就可以将求差分约束系统的最小解转化为求不等式组的最长路。

代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#define inf 100000007

using namespace std;

struct edge
{
	int v,w,next,last;
};

int n,m,x,y,z,l,r,tot=0;
int a[101000],dis[101000];
bool b[101000];
queue<int> q;
edge map[1010000];

void build(int x,int y,int z)
{
	tot++;
	map[tot].v=y;
	map[tot].w=z;
	map[tot].next=map[x].last;;
	map[x].last=tot;
	return;
}

int main()
{
	scanf("%d",&n);
	l=500000;	r=-1;
	for (int i=0;i<n;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		build(x-1,y,z);
		if (x<l)
			l=x;
		if (y>r)
			r=y;
	}
	for (int i=l;i<=r;i++)
	{
		build(i-1,i,0);
		build(i,i-1,-1);
	}
	int s=l-1;
	for (int i=1;i<=n;i++)
	{
		dis[i]=-inf;
		b[i]=false;
	}
	q.push(s);
	dis[s]=0; b[s]=true;
	while (!q.empty())
	{
		x=q.front();
		q.pop();
		int j=map[x].last;
		while (j!=0)
		{
			y=map[j].v;
			if (dis[y]<dis[x]+map[j].w)
			{
				dis[y]=dis[x]+map[j].w;
				if (!b[y])
				{
					q.push(y);
					b[y]=true;
				}
			}
			j=map[j].next;
		}
		b[x]=false;
	}
	cout<<dis[r]<<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

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

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

思路

对于第一名来说,他一场也没有输过,所以对于一行读入的P1,P2,我们可以记录一下每个点的入度,然后我们使用优先队列,将入度为0的点压入队列,因为我们要求要输出编号最小,所以我们可以对节点进行一个取负压入,然后将节点弹出,存入到答案数组中,当弹出一个点时,他所战胜的点,或者说他连出去的边可以视为无效的,所以要将他连接的点的入度-1,然后就出现了新的一批入度为0的节点,重复入队直至结束。

题目保证有唯一解,所以不然不会出现环路,即a>b,b>c,c>a,所以不然存在入读为0,使得这种循环解法可以解决该问题。

代码
#include<iostream>
#include<queue>

using namespace std;

struct edge
{
	int v,next,last;
} a[100100];

int n,m,tot,sum,ru[1010],ans[1010];

void build(int x,int y)
{
	tot++;
	a[tot].v=y;
	a[tot].next=a[x].last;
	a[x].last=tot;
}

int main()
{
	while (scanf("%d%d",&n,&m)!=EOF)
	{
		tot=0;
		for (int i=1;i<=n;i++)
			ru[i]=0;
		int x,y;
		for (int i=0;i<m;i++)
		{
			scanf("%d%d",&x,&y);
			build(x,y);
			ru[y]++;
		}
		priority_queue<int> q;
		for (int i=1;i<=n;i++)
			if (ru[i]==0)
				q.push(-i);
		sum=0;
		while (!q.empty())
		{
			x=0-q.top();
			q.pop();
			sum++;
			ans[sum]=x;
			int i=a[x].last;
			while (i!=0)
			{
				y=a[i].v;
				ru[y]--;
				if (ru[y]==0)
					q.push(-y);
				i=a[i].next;
			}
		}
		
		for (int i=1;i<n;i++)
			printf("%d ",ans[i]);
		printf("%d\n",ans[n]);
		
		for (int i=1;i<=n;i++)
			a[i].last=0;
		for (int i=1;i<=m;i++)
		{
			a[i].next=0;
			a[i].v=0;
		}
	}
	
	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开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!

思路

首先求出所有的强连通分量,然后进行缩点,然后求出度为0的点的支持票数,找出最多票数的强连通分量,输出该连通分量中的点。

求连通分量我们可以使用Kosaraju算法,先对图进行一遍DFS遍历,然后倒转边的方向,构建一个反图,对反图按照退出顺序的逆序进行第二次 DFS 遍历,每次遍历得到的那些点即属于同一个强连通分量。

缩点我们在求连通分量时使用一个sum表示当前在求第几个连通分量,f[i]表示第i个连通分量的起点是f[i],c[i]表示第i个节点属于第c[i]个连通分量,用一个二维数组s,s[i]表示第i个连通分量中的所有点。这样我们就完成了缩点。

然后我们从节点0开始遍历,新建一个二维数组e,e[i]表示所有能到达连通分量i的点。如果0可以到1而且0和1不是同一个连通分量,这样我们就将0所在的连通分量中的点全部加到e[c[i]]中,然后chu[c[0]]++,这样我们对大小就进行了一个初始化处理。

最后求出度为0的点的支持票数。

代码
#include<iostream>
#include<queue>
#include<vector>
#define N 5010

using namespace std;

int f[N],c[N],chu[N],k[N],tot,sum; 
bool b[N];
vector<int> map[N],fmap[N],e[N],s[N]; 

void dfs1(int x)										//第一次DFS遍历 
{
	b[x]=true;
	for (int i=0;i<map[x].size();i++)
		if (!b[map[x][i]])
			dfs1(map[x][i]);
	f[tot]=x;
	tot++;
}	
void dfs2(int x)										//按照退出顺序的逆序对反图进行第二次 DFS 遍历
{
	c[x]=sum;
	for (int i=0;i<fmap[x].size();i++)
		if (!c[fmap[x][i]])
			dfs2(fmap[x][i]);
	s[sum].push_back(x); 
}
void dfs3(int x, int &size)								//求连通分量的大小 
{
	b[x]=true;
	size=size+s[x].size();
	for (int i=0;i<e[x].size();i++)
		if (!b[e[x][i]])
			dfs3(e[x][i], size);
}

int main()
{
	int t,n,m,x,y,size,max_size;
	cin>>t;
	for (int ll=1;ll<=t;ll++)
	{
		scanf("%d%d",&n,&m);
		sum=0;	tot=0;
		for(int i=0;i<n;i++)
		{
			c[i]=0;
			f[i]=0;
			b[i]=false;
			map[i].clear();
			fmap[i].clear();
		}	
		for(int i=0;i<m;i++)
		{
			scanf("%d%d",&x,&y);
			map[x].push_back(y);
			fmap[y].push_back(x);
		}
		for (int i=0;i<n;i++)
			if (!b[i])
				dfs1(i);
		for (int i=tot-1;i>=0;i--)
			if (c[f[i]]==0)
			{
				sum++;
				dfs2(f[i]);	
			}	
		for (int i=1;i<=sum;i++)
		{
			chu[i]=0;
			e[i].clear();
		}
		for (int i=0;i<n;i++)
			for (int j=0;j<map[i].size();j++)
				if (c[i]!=c[map[i][j]])
				{
					e[c[map[i][j]]].push_back(c[i]);
					chu[c[i]]++;
				}
		size=0;
		max_size=0;
		for (int i=1;i<=sum;i++)
		{
			size=0;
			for (int j=0;j<n;j++)
				b[j]=false;
			if (chu[i]==0)
				dfs3(i,size);
			k[i]=size;
			if (size>max_size)
				max_size=size;
		}
		priority_queue<int> q; 
		for (int i=1;i<=sum;i++)
			if (k[i]==max_size)
				for (int j=0;j<s[i].size();j++)
					q.push(-s[i][j]);
		printf("Case %d: %d\n",ll,max_size-1);
		printf("%d",-q.top());
		q.pop();
		while(!q.empty())
		{
			printf(" %d",-q.top());
			q.pop();	
		}
		printf("\n");
		for(int i=1;i<=sum;i++)
			s[i].clear();
	}
	
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值