奖金-拓扑排序--队列-邻接表做法

本文介绍了一种基于有向图和拓扑排序的奖金分配算法,旨在根据员工贡献和代表意见,寻找满足条件的最小总奖金分配方案。

奖金

题目描述
由于公司在2013年的销售业务成绩优秀,公司总经理心情大好,决定给每位员工发奖金。公司决定以每个人本年在公司的贡献为标准来计算他们得到奖金的多少。于是总经理下令召开 m 方会谈。每位参加会谈的代表提出了自己的意见:“我认为员工 a 的奖金应该比 b 高!”。总经理决定要找出一种奖金方案,满足各位代表的意见,且同时使得总奖金数最少。每位员工奖金最少为100元。 

输入格式
第一行两个整数 n 和 m,表示员工总数和代表数;
接下来有 m 行,每行 2 个整数 a 和 b,表示某个代表认为第 a 号员工奖金应该比第 b 号员工高。 

输出格式
若无法找到合理方案,则输出“Poor Xed”;否则输出一个数表示最少总奖金。

样例数据 1
输入  [复制]

2 1 
1 2
输出

201
备注
【数据规模】
80%的数据满足:n<=1000,m<=2000;
100%的数据满足:n<=10000,m<=20000。


算法分析:

如果把每个人当成点,则“我认为员工a的奖金应该比b高”就意味着点与点之间有先后关系,从b因一条线到a。这样就可以画出一个有向图。当能找到一个拓扑排序时就说明可以找到一种方案,但如果找不到一个拓扑排序,就说明这个有向图有环,找不到合适的方案。问题解决。

举个例子来看:

样例:

5  4      

4  2      

3  4      

5  4      

3  5

对应的图为:

拓扑排序的规则:入度为0的入队列,所有在每一个点入队列 时就可以确定他的钱数。即1,2入队列时他们是初始值100, 4入队列时,他的是2的钱数+1.依次类推。所以用数组mon[i]表示第i个人的奖金数则   :     

mon[1]=mon[2]=100;        

mon[4]=mon[2]+1;        

mon[5]=mon[4]+1;        

mon[3]=mon[5]+1;

代码为:


#include<iostream>
#include <cstdio>
#include<algorithm>
#include<queue>
using namespace std;
 
int r[10005],m,n,money;
queue<int> q;//队列 
//邻接表存储
struct edge{
	int y,nex;
}e[20005];
int head[10005],mon[10005],num=0; 
bool topsort()                  //拓扑排序
{
  int tot,cs=0;
  tot=0;//记录出队列的元素个数,判断是否有环 
  for(int i=1;i<=n;i++)
   if(!r[i]) {
   	q.push(i);
   	mon[i]=100;//初始入度为0的点,钱数为200
	money+=mon[i]; 
   }
  while(!q.empty())                  //tot顶点个数
  {
     int x=q.front();
	 tot++;
	 q.pop();
	 for(int i=head[x];i;i=e[i].nex)
	 {
	 	int yy=e[i].y;
	 	r[yy]--;//把所有从x到达的边的入度都减去1;
		 if(!r[yy])//如果yy的入度为0,则说明从x到y的这条边是最后一条边,yy的钱数只需要比x多1元即可 
		 {
		 	q.push(yy);
			 mon[yy]=mon[x]+1;
			 money+=mon[yy]; 
		 } 
	 }	  
  }
  if(tot==n) return true;
  else  return false; 
}
void addedge(int x,int y)
{
	num++;
	e[num].y=y;
	e[num].nex=head[x];
	head[x]=num;
} 
 
int main()
{
   int a,b;
  cin>>n>>m;
  for(int i=1;i<=m;i++)
   {
     scanf("%d%d",&a,&b);//a比b高,则从b到a有条边 
     addedge(b,a);//b到a添加边 
     r[a]++;        //统计入度
   }
  money=0;
  if(topsort())cout<<money<<endl;
     else cout<<"Poor Xed"<<endl;
  return 0;
}

网上有很多题解,但方法与我不同,多多思考,总是会有意想不到的惊喜。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值