[HNOI2006]超級英雄Hero [Scoi2010]遊戲 匈牙利演算法 解題報告
阿新 • • 發佈:2019-02-07
這兩道題是極為類似的,只是一道 n <1001,另一道 n <10000 好像區別不大
你有n個物品,每個物品有兩個值且每個物品只能使用一次,每次使用可以選擇它的其中一個值。
提問:使用一些物品後,能取到的最大的連續值是多少?
以 [HNOI2006]超級英雄Hero 為例:有六個物品,其對應值分別是
3 2
2 0
0 3
0 4
3 2
3 2
當分別選擇<3,2,0,4>可過前四關,且這也是最優解(之一)
此時可選物品和對應值形成了一個 “二分圖”
二分圖匹配——匈牙利演算法
匈牙利演算法的基本內容是:為該點尋找一個匹配,並且其他已匹配點仍有匹配
定義一個bool f(v) ,v 指新的匹配點,返回能否匹配成功。
先列舉v的每一個未在遞迴過程中被搜尋過的節點,如果其未被匹配,則匹配,否則呼叫f(其連線節點)以重新分配。
核心程式碼:
bool f(int v) {
for (int i=0;i<u[v].size();i++)
if (!vis[rt = u[v][i]]) {
vis[rt] = true;
if ((!q[rt]) || f(q[rt])) return q[rt] = v;
} return false;
}
[HNOI2006]超級英雄Hero :
#include <cstdio>
#include <cstring>
#include <vector>
int n,m,i,q[1010] ,rt,t1,t2;
bool vis[1010];
std::vector <int> u[1010];
bool f(int v) {
for (int i=0;i<u[v].size();i++)
if (!vis[rt = u[v][i]]) {
vis[rt] = true;
if ((!q[rt]) || f(q[rt])) return q[rt] = v;
} return false;
}
int main() {
scanf("%d%d",&n,&m);
for (i=1;i<=m ;i++) {
scanf("%d%d",&t1,&t2);
u[i].push_back(t1); u[i].push_back(t2);
}
for (i=1;i<=m;i++) if (memset(vis,0,sizeof(vis)) && !f(i)) break;
printf("%d\n",i - 1);
}
[Scoi2010]遊戲:(這題memset會超時 於是開int vis[]記錄)
#include <cstdio>
#include <cstring>
int n,i,q[1000010],head[1000010],last[1000010],rt,t1,t2,ans,num;
int vis[1000010],id;
struct ljb {int to,nxt;} u[2000010];
bool f(int v) {
for (int i=head[v];i;i=u[i].nxt) if (vis[rt = u[i].to] ^ id) {
vis[rt] = id;
if ((!q[rt]) || f(q[rt])) return q[rt] = v;
} return false;
}
int main() {
for (scanf("%d",&n),i=1;i<=n;i++) {
scanf("%d%d",&t1,&t2);
u[++num].to = i; last[t1] = last[t1] ? u[last[t1]].nxt = num : head[t1] = num;
u[++num].to = i; last[t2] = last[t2] ? u[last[t2]].nxt = num : head[t2] = num;
} while (f(id = ++ans));
printf("%d\n",ans - 1);
}