hdu 2647 Reward【拓扑排序】
程序员文章站
2022-05-01 14:26:56
...
题目大意:给你n个人,m个关系,表示a比b的奖金要多。问最少分配的奖金总数是多少。
思路:假如有这样一个图:
( 箭头表示1比2奖金要多,1比5奖金要多)
不难看出,标号3的那层都是888的奖金,标号2的那层都是889的奖金,标号1的那成是890的奖金,如果我们想要确定每个点权值的话,我们需要知道各个节点之间的全序关系,也就不难想到使用拓扑排序来做这个题,直接能够想到的方法就是直接拆点拆边,完成拓扑排序,如果不满足拓扑序输出-1.那么如果满足拓扑序呢?每个节点拆出来的层数来如何判断呢?每个点权值如何判断呢?这个时候我们其实不用一直憋牛角尖正向思维去做,我们不妨反着来考虑,反向建造图,也就相当于在原图中反着拆点拆边。
我们将图建成这样:
这个时候度为0的点也就是分配888奖金的点,也就首先确定了一些点的点权值,接着我们拆边拓扑,也就不难想到,如果当前点u拆边之后使得一个点v的度为0,辣么这个点v的点权值也就比u的点权值多1.这个时候问题就全部解决辣.
最后处理代码问题:
10000*10000的邻接矩阵会超内存,我们使用邻接表实现即可:
#include<stdio.h>
#include<queue>
#include<string.h>
using namespace std;
#define maxn 200010
int head[maxn];
struct EdgeNode
{
int to;
int w;
int next;
} e[maxn];
int vis[maxn];
int price[maxn];
int degree[maxn];
void add(int x,int y,int i)
{
e[i].to=x;
e[i].next=head[y];
head[y]=i;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
queue<int >s;
memset(price,0,sizeof(price));
memset(degree,0,sizeof(degree));
memset(vis,0,sizeof(vis));
memset(head,-1,sizeof(head));
for(int i=0; i<m; i++)
{
int x,y;
scanf("%d%d",&x,&y);
degree[x]++;
add(x,y,i);
}
int cont=0;
for(int i=1;i<=n;i++)
{
if(degree[i]==0)
{
s.push(i);
vis[i]=1;
price[i]=888;
cont++;
}
}
while(!s.empty())
{
int u=s.front();s.pop();
for(int j=head[u];j!=-1;j=e[j].next)
{
int v=e[j].to;
degree[v]--;
if(degree[v]==0&&vis[v]==0)
{
vis[v]=1;
s.push(v);
price[v]=price[u]+1;
cont++;
}
}
}
if(cont==n)
{
int sum=0;
for(int i=1;i<=n;i++)
{
sum+=price[i];
}
printf("%d\n",sum);
}
else
{
printf("-1\n");
}
}
}
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#include <limits.h>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <set>
#include <string>
#define l long long
#define ms(a) memset(a,0,sizeof(a))
#define pi acos(-1.0)
#define INF 0x3f3f3f3f
const double E=exp(1);
const int maxn=1e4+10;
using namespace std;
int n,m;
vector<int> v[maxn];
int vis[maxn];
int money[maxn];
bool toposort()
{
int ans=0;
queue<int>que;
for(int i=1;i<=n;i++)
{
// 把刚开始入度为0的点放入
if(!vis[i])
que.push(i);
}
while(!que.empty())
{
int res=que.front();
que.pop();
// 每次pop出去的点都是入度为0的点,统计这些点的个数
ans++;
for(int i=0;i<v[res].size();i++)
{
vis[v[res][i]]--;
if(vis[v[res][i]]==0)
{
// 将入度为0的点加入队列
que.push(v[res][i]);
// 更新一下后继点的工资,有点难理解,建议画一下图找几组样例试一下
money[v[res][i]]=max(money[res]+1,money[v[res][i]]);
}
}
}
if(ans==n)
return true;
else
return false;
}
int main(int argc, char const *argv[])
{
ios::sync_with_stdio(false);
int x,y;
while(cin>>n>>m)
{
ms(vis);
ms(money);
for(int i=0;i<maxn;i++)
v[i].clear();
for(int i=0;i<m;i++)
{
cin>>x>>y;
// 反向建图
v[y].push_back(x);
vis[x]++;
}
if(!toposort())
cout<<-1<<endl;
else
{
ll ans=888*n;
for(int i=1;i<=n;i++)
ans+=money[i];
cout<<ans<<endl;
}
}
return 0;
}
上一篇: 初级算法探索——字符串篇(二)
下一篇: HDU1106 排序 【自动机】