1. 程式人生 > 實用技巧 >Ordering Cows

Ordering Cows

題意描述

好像找不到連結(找到了請聯絡作者謝謝),所以題目描述會十分詳細:

Problem 1: Ordering Cows [Bruce Merry, South African Computer Olympiad, 2003]

Farmer John has noticed that when his N (1 <= N <= 1,500) cows line up to be milked, certain cows always line up ahead of certain other cows. He has made a list of L (1 <= L <= 10,000) pairs of cows for which he has noticed this. He wants to send this list to his friend, Farmer Bob. Unfortunately, the inter-farm cowmunication network is very slow.

FJ has realized that he can compress the list by removing redundant information. The information he has is all of the form "cow A lines up before cow B". Information is redundant if it can be deduced from
other information on the list. For example, given "Alice is before Betty", "Betty is before Carol" and "Alice is before Carol" the last statement is redundant. Of course, the cows are numbered with unique serial numbers in the range 1..N; only a few of them have names (and those are not used here).

Help Farmer John by finding the smallest subset of his list from which the rest of the list can be deduced. Happily, the answer is guaranteed to be unique, and the original list contains no contradictions such as "Alice before Betty" and "Betty before Alice".

PROBLEM NAME: order

INPUT FORMAT:

  • Line 1: Two space-separated integers: N and L.

  • Lines 2..L+1: Two integers X and Y (1 <= X,Y <= N), indicating that cow X lines up before cow Y. No fact is duplicated.

SAMPLE INPUT (file order.in):

5 6
3 5
4 2
5 2
2 1
3 1
4 1

OUTPUT FORMAT:

  • Line 1: A single line with the number of facts that Farmer John will send to Farmer Bob.

  • Lines 2..U+1: The facts from the input file that Farmer John will transmit. These must be lists in the same format as the entries in the input file. They must be sorted numerically on the first entry in the line, with ties being broken by the second entry on the line.

SAMPLE OUTPUT (file order.out):

4
2 1
3 5
4 2
5 2

OUTPUT DETAILS:

The facts "cow 3 before cow 1" and "cow 4 before cow 1" can be deduced from the 4 facts listed in the output. However, none of the output facts can be deduced from the others.

給你關於 \(N\) 個數的 \(L\) 對大小關係,其中關係 x y 表示 x>y

如果有關係 x y 與關係 y z 那麼顯然關係 x z 是不必要的。

求出這 \(L\) 對關係中的最少必要關係。(即通過省下的這些關係可以推出被刪去的關係)

寫出其數量並按照以數字一為主關鍵字,以數字二為次關鍵字的順序從小到大輸出。

演算法分析

類似傳遞閉包,但是這裡要求你排除冗餘的關係。

那麼顯然,如果我們可以確定一個順序,保證後面的關係不會影響前面的關係。

那麼只需要從前往後掃一遍,遇到多餘的關係排除即可。(因為只有前面的關係可能影響後面的關係)

排除的方法之後再說。

確定方法的關係:

  1. 將每個關係當做有向邊建圖,易證這是個 DAG 圖。
  2. 將此圖拓撲排序,按照拓撲排序的順序給各個節點從小到大賦值。
  3. 按照每個關係的數字一拓撲值從大到小為主關鍵字,數字二拓撲值從小到大為次關鍵字排序。
  4. 所得即上面所說的遍歷順序。

解釋一下,我們給每個節點賦予一個 ”貢獻值“,表示其對其它節點的影響力。

那麼顯然影響大的邊要先考慮(這也符合上面的順序)。

易得,拓撲值大的節點影響力也大,所以以其從大到小為主關鍵字。

但是,當從同一個點出發考慮關係時,就應當優先考慮拓撲值小的,理由如下:

那麼顯然只有按照以從小到大為次關鍵字,才能將關係 1 3 排除。

然後就按照這個順序往後排查即可,具體方法為狀壓 DP。

但是由於節點數量很多,所以要用到 C++ STL 的 bitset。

假設有關係 \(x\to y\),如果 \(bits(x,y)\) 這個關係為多餘關係。

否則 \(bits(x)|=bits(y)\)

程式碼實現

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<bitset>
#include<queue>
#define N 1510
#define M 10010
using namespace std;

int n,m,head[N],cnt=0;
int ru[N],Topo[N],tot=0;
struct Edge{
	int nxt,to,frm;
}ed[M];
struct Ans{
	int u,v;
}ans[M];
bitset<N>bits[N];
queue<int>q;

int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
	while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
	return x*f;
}

void add(int u,int v){
	ed[++cnt].nxt=head[u];
	ed[cnt].frm=u;
	ed[cnt].to=v;
	head[u]=cnt;
	return;
}

void topo(){
	for(int i=1;i<=n;i++)
		if(!ru[i]) q.push(i);
	while(!q.empty()){
		int u=q.front();q.pop();
		Topo[u]=++tot;
		for(int i=head[u];i;i=ed[i].nxt)
			if(!--ru[ed[i].to]) q.push(ed[i].to);
	}
	return;
}

bool cmp(Edge a,Edge b){
	if(a.frm!=b.frm) 
		return Topo[a.frm]>Topo[b.frm];
	return Topo[a.to]<Topo[b.to];
}

bool cnp(Ans a,Ans b){
	if(a.u!=b.u) return a.u<b.u;
	return a.v<b.v;
}

int main(){
	//freopen("order.in","r",stdin);
	//freopen("order.out","w",stdout);
	memset(ru,0,sizeof(ru));
	n=read(),m=read();
	for(int i=1;i<=m;i++){
		int u=read(),v=read();
		add(u,v);++ru[v];
	}
	topo();//簡簡單單的拓撲排序。
	sort(ed+1,ed+m+1,cmp);
	for(int i=1;i<=n;i++) 
		bits[i][i]=true;//預處理。
	tot=0;
	for(int i=1;i<=m;i++){
		int u=ed[i].frm,v=ed[i].to;
		if(bits[u][v]) continue;
		bits[u]=bits[u]|bits[v];//DP。
		ans[++tot]=(Ans){u,v};
	}
	sort(ans+1,ans+tot+1,cnp);
	printf("%d\n",tot);
	for(int i=1;i<=tot;i++)
		printf("%d %d\n",ans[i].u,ans[i].v);
	//fclose(stdin);fclose(stdout);
	return 0;
}

完結撒花。