题目:
http://www.lydsy.com/JudgeOnline/problem.php?id=1191
http://www.lydsy.com/JudgeOnline/problem.php?id=1854
这两道题是极为类似的,只是一道 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);
}