双子树

这一天,随身带来的干粮豆吃玩了,必须有人去找些食物来吃才行。可Z4的懒惰是出了名的,谁会愿意去呢?经过一番商讨,大家最后决定以老规矩“猜十次”的方式决定人选。可怜的立方由于猜拳技术不过关,屡战屡败,只好踏上觅食的征途。
立方后来找到了两棵奇怪的树,由于它们的形态十分相像,双子座的立方一时兴起便把它们命名为“双子树”。可双子树的果实能不能吃呢?“管它呢,先摘了再说。”于是身手敏捷的立方便爬上其中一棵,摘下一个大大的果实。可就在此时,另一棵却有几个果实坠地。
立方细心一看,发现这双子树上的某些果实有一些细丝相连,只要摘下其中一棵树的某一个果实,另一棵树将会有相应的一个或多个(也可能没有)果实坠地而摔坏,这些果实都和摘下的果实用细丝相连。摔坏的果实当然不能吃了。而且,立方发现,那些细丝是没办法弄断的,除非摘下它两端的果实的其中一个。由于这时只有立方一人(好没义气的Z4啊……),他只能眼睁睁地看着它们摔坏。也就是说,立方无法同时摘取任一条细丝两端上的两个果实。于是,不同的摘法最后会得到不同数量的果实,而立方将会用随身带的一个容量为V(表示能装V个果实)的大袋子将它们装好。
馋嘴的立方当然希望摘得越多越好啦,那么,他最多可以得到多少个果实呢?
特别地,任两个果实间最多只会有一条细丝相连,同一棵树上的果实间不会有细丝相连,当袋子装满后,立方的口还可以塞进一个。

输入

输入文件tree.in的第一行包括四个整数V(0<=V<=1000),N1,N2(0<=N1,N2<=500)和M,分别表示袋子的容量,第一棵树上的果实个数,第二棵树上的果实个数和细丝总数。为了方便计算,立方人为地把果实分别按1..N1和1..N2标号。
接下来有M行,每行有两个整数A,B(1<=A<=N1,1<=B<=N2),表示第一棵树上的果实A和第二棵树上的果实B有一条细丝相连。

输出

输出文件tree.out仅有一个整数,表示立方最多能得到的果实个数。

求二分图最大独立集等于求总点数-二分图最大匹配,因为二分图最大匹配完后没有匹配的点显然可以算进去,匹配的一对点只能取一个。(写网络流不知为何tle90,还是匈牙利好)

#include<bits/stdc++.h>
using namespace std;
int v,n1,n2,m,belong[1010];bool vis[1010];
vector<int>mp[510];
bool check(int x)
{
    for(int i=0;i<mp[x].size();i++)
    {
        if(!vis[mp[x][i]])
        {
            vis[mp[x][i]]=1;
            if(!belong[mp[x][i]]||check(belong[mp[x][i]]))
            {
                belong[mp[x][i]]=x;
                return 1;
            }
        }
    }
    return 0;
}
int main()
{
    int ans=0,u,v,V;
    scanf("%d%d%d%d",&V,&n1,&n2,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        mp[u].push_back(v+n1);
    }
    for(int i=1;i<=n1;i++)
    {
        memset(vis,0,sizeof vis);
        if(check(i))ans++;
    }
    cout<<min(V+1,n1+n2-ans);
}

猜你喜欢

转载自blog.csdn.net/caoyang1123/article/details/81635272