1. 程式人生 > >[SCOI2010] 連續攻擊問題

[SCOI2010] 連續攻擊問題

edge 擁有 攻擊力 裝備 2個 很多 個數字 釋放 names

題目

Description

lxhgww最近迷上了一款遊戲,在遊戲裏,他擁有很多的裝備,每種裝備都有2個屬性,這些屬性的值用[1,10000]之間的數表示。當他使用某種裝備時,他只能使用該裝備的某一個屬性。並且每種裝備最多只能使用一次。遊戲進行到最後,lxhgww遇到了終極boss,這個終極boss很奇怪,攻擊他的裝備所使用的屬性值必須從1開始連續遞增地攻擊,才能對boss產生傷害。也就是說一開始的時候,lxhgww只能使用某個屬性值為1的裝備攻擊boss,然後只能使用某個屬性值為2的裝備攻擊boss,然後只能使用某個屬性值為3的裝備攻擊boss……以此類推。現在lxhgww想知道他最多能連續攻擊boss多少次?

Input

輸入的第一行是一個整數N,表示lxhgww擁有N種裝備接下來N行,是對這N種裝備的描述,每行2個數字,表示第i種裝備的2個屬性值

Output

輸出一行,包括1個數字,表示lxhgww最多能連續攻擊的次數。

Range

對於30%的數據,保證N < =1000

對於100%的數據,保證N < =1000000

Solution

二分圖的最大匹配問題,做法很巧妙,但是很難想到。

第一眼看到這個題想到的是將某個物品的兩個屬性分成左右部點,但是很難解決本題,尤其是在處理一個物品只能用一種屬性的時候。所以我們不妨換一種思路,對於物品 i 的屬性a,b,分別從a和b向i連一條有向邊。將物品的屬性當做左部點,編號當做右部點,求最大匹配即可。

這樣為什麽是正確的呢?我們可以考慮匈牙利算法的具體過程:在匹配值為 i 的技能時,那麽 1~i-1 的屬性肯定已經匹配完成,所以如果 i 對應的編號 j 被匹配了的話,那麽就讓匹配 j 的那個屬性 p 再去找別的物品標號匹配,形象地說,就是用別的物品來釋放攻擊力為 p 的這個技能,用 j 這個物品釋放攻擊力為 i 的技能。如果找到這樣一條增廣路,那麽就說明當前可以匹配,ans++。

Code

#include<cstdio>
#include<cstring>
using namespace std;

int n,cnt,ans;
bool vis[10005];
int
head[10005]; int pre[1000005]; struct Edge{ int to,nxt; }edge[2000005]; void add(int x,int y){ edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } bool dfs(int now){ if(vis[now]) return 0; vis[now]=1; for(int i=head[now];i;i=edge[i].nxt){ if(!pre[edge[i].to]||dfs(pre[edge[i].to])){ pre[edge[i].to]=now; return 1; } } return 0; } signed main(){ scanf("%d",&n); for(int x,y,i=1;i<=n;i++){ scanf("%d%d",&x,&y); add(x,i),add(y,i); } for(int i=1;i<=10000;i++){ memset(vis,0,sizeof vis); if(dfs(i)) ans++; else break; } printf("%d",ans); return 0; }

[SCOI2010] 連續攻擊問題