hdu 5195 BC#35 拓扑排序 优先队列 重复入队的想法 十字链表

题意 求可以删除K条边的DAG图的最大字典序的拓扑序列

思路 用优先队列维护入度小于等于K的点(注意入队后,不改变K),BFS,每次看堆顶元素i的入度是否小于等于当前的K,是则K减入度,删去到i的边,继续从i搜索,否则出队,继续看堆顶元素,重复上述步骤。继续搜到的点入度如果小于等于当前K则入队,注意维护是否入队的数组,不要重复入队~


注意点:(1)拓扑排序删边,不用真删,只要维护入度数组减一就行!!!

   (2)就算是要真删,也可以用个is_del数组来表示这边是否真的删去,没必要从链表里删掉!

   (3)我这题为了删去所有到一个点的边还写了个十字链表。。。真是二了。。。

                             不过可以当做今后的十字链表模板

   (4)注意维护in_q这个数组,防止队中有重复元素

//#include <iostream>
#include <stdio.h>
#include <cmath>
#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdlib>
#include <map>
using namespace std;
#define I64_MAX 9223372036854775807 
typedef long long ll;
const double pi=acos (-1.0);
const double eps=1e-8 ;
//const ll INF=(I64_MAX)/2;
//#pragma comment(linker, "/STACK:102400000,102400000")
const int inf=0x3f3f3f3f ;
#define maxx(a) memset(a, 0x3f, sizeof(a))
#define zero(a) memset(a, 0, sizeof(a))
#define FILL(a,b) memset(a, b, sizeof(a))
#define REP(i,a,b) for(i=a;i<b;i++)
#define rep(i,n) REP(i,0,n)
#define srep(i,n) for(i = 1;i <= n;i ++)
#define snuke(c,itr) for( __typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)
#define MP make_pair
#define fi first
#define se second
typedef pair <int, int> PII;
typedef pair <ll, ll> PX;
typedef pair<int,ll> PIL;
#define maxn 100005

int n,m;

int ru[maxn];
//int chu[maxn];
priority_queue<int> q;
queue<int> q_print;
int in_q[maxn];
struct Edge{
	int u,v,next,pre,rnext,rpre;
	void set(int uu=0,int vv=0,int nextt=-1,int rnextt=-1,int pree=-1,int rpree=-1)
	{
		this->u = uu;
		this->v = vv;
		this->next = nextt;
		this->pre = pree;
		this->rnext = rnextt;
		this->rpre = rpree;
	}
}edge[maxn];

int g[maxn];
int rg[maxn];

void init()
{
	memset(g,-1,sizeof(g));
	memset(rg,-1,sizeof(rg));
//	memset(chu,0,sizeof(chu));
	memset(ru,0,sizeof(ru));
	memset(in_q,0,sizeof(in_q));
	while(q.size()>0)
		q.pop();
	while(q_print.size()>0)
		q_print.pop();
}

void addEdge()
{
	for(int i=0;i<m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		edge[i].set(u,v,g[u],rg[v]);
		g[u] = i;
		rg[v] = i;
		if(edge[i].next != -1)
			edge[edge[i].next].pre = i;
		if(edge[i].rnext != -1)
			edge[edge[i].rnext].rpre = i;
		ru[v]++;
	}
}

void del(int e)
{
	if(edge[e].pre == -1)
	{
		g[edge[e].u] = edge[e].next;
	}
	else
	{
		edge[edge[e].pre].next =  edge[e].next;
	}
	if(edge[e].rpre == -1)
	{
		rg[edge[e].v] = edge[e].rnext;
	}
	else
	{
		edge[edge[e].rpre].rnext =  edge[e].rnext;
	}
	if(edge[e].next != -1)
	{
		edge[edge[e].next].pre = edge[e].pre;
	}
	if(edge[e].rnext != -1)
	{
		edge[edge[e].rnext].rpre = edge[e].rpre;
	}
	//return edge[e].next;
}

void delAll(int i)
{
	for(int e=rg[i];e!=-1;e=edge[e].rnext)
	{
		del(e);
	}
}

int main ()
{
	#ifdef LOCAL
    // freopen("E:\\input.txt" ,"r", stdin);
     // freopen ("E:\\out.txt","w",stdout);
	#endif
	int i,j;
	int K;
	while(scanf("%d%d%d",&n,&m,&K)==3)
	{
		init();
		addEdge();
		for(i=n;i>=1;i--)
		{
			if(ru[i] <= K)
			{
				q.push(i);
				in_q[i] = 1;
			}
		}
		while(q.size() > 0)
		{
			int tmp;
			while(1)
			{
				tmp = q.top();
				q.pop();
				in_q[tmp] = 0;
				if(ru[tmp] <= K)
				{
					delAll(tmp);
					K -= ru[tmp];
					ru[tmp] = 0;
					break;
				}
			}
			q_print.push(tmp);
			for(int e=g[tmp];e!=-1;e=edge[e].next)
			{
				ru[edge[e].v]--;
				if(ru[edge[e].v] <= K && in_q[edge[e].v]==0)
				{
					q.push(edge[e].v);
					in_q[edge[e].v] = 1;
				}
				del(e);
			}
		}
		while(q_print.size()>1)
		{
			int tmp = q_print.front();
			printf("%d ",tmp);
			q_print.pop();
		}
		if(q_print.size() == 1)
		{
			int tmp = q_print.front();
			printf("%d\n",tmp);
			q_print.pop();
		}
	}

	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值