1. 程式人生 > >BZOJ 1143 祭祀river(floyd傳遞閉包+最大獨立集)

BZOJ 1143 祭祀river(floyd傳遞閉包+最大獨立集)

1143: [CTSC2008]祭祀river

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 3152  Solved: 1617
[Submit][Status][Discuss]

Description

  在遙遠的東方,有一個神祕的民族,自稱Y族。他們世代居住在水面上,奉龍王為神。每逢重大慶典, Y族都 會在水面上舉辦盛大的祭祀活動。我們可以把Y族居住地水系看成一個由岔口和河道組成的網路。每條河道連線著 兩個岔口,並且水在河道內按照一個固定的方向流動。顯然,水系中不會有環流(下圖描述一個環流的例子)。

 

  由於人數眾多的原因,Y族的祭祀活動會在多個岔口上同時舉行。出於對龍王的尊重,這些祭祀地點的選擇必 須非常慎重。準確地說,Y族人認為,如果水流可以從一個祭祀點流到另外一個祭祀點,那麼祭祀就會失去它神聖 的意義。族長希望在保持祭祀神聖性的基礎上,選擇儘可能多的祭祀的地點。

Input

  第一行包含兩個用空格隔開的整數N、M,分別表示岔口和河道的數目,岔口從1到N編號。接下來M行,每行包 含兩個用空格隔開的整數u、v,描述一條連線岔口u和岔口v的河道,水流方向為自u向v。 N ≤ 100 M ≤ 1 000

Output

  第一行包含一個整數K,表示最多能選取的祭祀點的個數。

Sample Input

4 4
1 2
3 4
3 2
4 2

Sample Output

2

【樣例說明】
在樣例給出的水系中,不存在一種方法能夠選擇三個或者三個以上的祭祀點。包含兩個祭祀點的測試點的方案有兩種:
選擇岔口1與岔口3(如樣例輸出第二行),選擇岔口1與岔口4。
水流可以從任意岔口流至岔口2。如果在岔口2建立祭祀點,那麼任意其他岔口都不能建立祭祀點
但是在最優的一種祭祀點的選取方案中我們可以建立兩個祭祀點,所以岔口2不能建立祭祀點。對於其他岔口
至少存在一個最優方案選擇該岔口為祭祀點,所以輸出為1011。

HINT

        2017南寧ICPC M題的原題,果然是一模一樣……

        都是我的錯,沒有一眼就看出來,導致兩個隊友一直在往歪的方向上思考這道題,然後與銀牌失之交臂……

        當時也曾經想過求二分圖的最大獨立集,用網路流或者匹配。但是我始終無法說服自己,這個圖怎麼能夠轉化為二分圖呢?但是實際上這個擔心是多餘的,我完全可以用慣用套路,把一個點拆成兩個,強制轉換為一個二分圖。然後直接用總的點數n減去最大匹配數,即為最後的最大獨立集。但是呢,這裡的連邊關係不是簡單的就用題目給出來的邊就行了,而是要先傳遞閉包,用傳遞性處理偏序關係。所以先進行floyd傳遞閉包,然後再用匹配即可。

#include <bits/stdc++.h>
#define N 110

using namespace std;

bool f[N][N];
int n,m;

namespace Hungry
{
    struct edge{int x,y,next;} g[N*N];
	int link[N],ls[N],e=0;
	bool cover[N];

	inline bool find(int i)
	{
		int k=ls[i];
		while (k>0)
		{
			if (!cover[g[k].y])
			{
				cover[g[k].y]=1;
				if (find(link[g[k].y])||(link[g[k].y]==0))
				{
					link[g[k].y]=i;
					return 1;
				}
			} k=g[k].next;
		}
		return 0;
	}

	inline int hungry()
	{
		int ans=0;
		for(int i=1;i<=n;i++)
		{
			memset(cover,0,sizeof(cover));
			if (find(i)) ans++;
		}
		return ans;
	}

	inline void addedge(int x,int y)     //  x==y is available
	{
		g[++e]=edge{x,y,ls[x]}; ls[x]=e;
	}
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        f[x][y]=1;
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
        if (i!=k&&f[i][k])
            for(int j=1;j<=n;j++)
            if (i!=j&&j!=k&&f[k][j]) f[i][j]=1;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if (f[i][j]) Hungry::addedge(i,j);
    printf("%d\n",n-Hungry::hungry());
}