Gym-101981I Magic Potion(二分图/网络流)

Gym-101981I Magic Potion(二分图/网络流)

题意

n个英雄m个怪,每个英雄只能打 m i m_i mi种怪中的一个,磕了药(且每个英雄最多磕一瓶)可以额外打死一头怪,现在有k瓶药,问最多打死多少只怪。

思路

和阿忠哥一看,匈牙利直接莽两遍不就有了,第一遍匹配没有嗑药打死几个。第二遍把第一遍打死了的剔除,再匹配磕了药打死几个(最多多打死k个)。但是wa on test7,gym和普通CF不一样我还看不到数据,气死。

但是我咨询了分别用匈牙利和网络流两种办法过了这题的宁宁姐和凉凉,宁宁表示匈牙利需要排个序才能过,不然第一遍的最优结果可能并不能导致第二遍最优。凉凉说他考虑到了匈牙利的这一点,所以决定用最大流去跑。

最大流的思路也不复杂,我看了大佬的题解,也不难想到。首先从超级源点出发和所有的英雄连一个容量为1的边(每个英雄不嗑药的情况下最多打死一个怪),所有英雄和对应能打死的怪之间也连容量为1的边,所有怪和超级汇点连一条边(表示所有的怪最多打死一次)。额外再做一个点,用来表示嗑药状态,超级源点和嗑药点之间建一条容量为k的边(表示最多k瓶药),嗑药点和所有英雄连一个容量为1的边(表示英雄最多磕1瓶药)。超级源点我给了标号0,超级汇点我给了编号n+m+1,嗑药点我给了编号n+m+2,所有怪的编号都在本身的基础上+n(和n个英雄区分开),注意数组开1002以上(两倍的点)。

代码(匈牙利)这里是wa的代码,AC代码请往下看

匈牙利失败尝试 wa on test 7,日后排序再试试,反正比赛的时候我们这个算法怎么也找不到反例,自闭了

#include<bits/stdc++.h>
using namespace std;
#define ll long long
struct edge{
    int from;
    int to;
    int nxt;
    edge(int f,int t,int n):from(f),to(t),nxt(n){}
};
vector<edge> edges;
vector<edge> edgem;
int n,m,k;
int egs[505];
int egm[505];
void addedge(int f,int t){
    edges.emplace_back(f,t,egs[f]);
    egs[f]=edges.size()-1;
}
void addedgem(int f,int t){
    edgem.emplace_back(f,t,egm[f]);
    egm[f]=edgem.size()-1;
}
bool vis[505];
int linker[505];
bool drugged[505];
bool finder(int num){
    if(num==-1){
        return true;
    }
    for(int i=egs[num];i!=-1;i=edges[i].nxt){
        int nxt=edges[i].to;
        if(!vis[nxt]){
            vis[nxt]=true;
            if(finder(linker[nxt])){
                linker[nxt]=num;
                return true;
            }
        }
    }
    return false;
}
int hungary(){
    int ans=0;
    memset(linker,-1,sizeof linker);
    for(int i=1;i<=m;i++){
        memset(vis,0,sizeof vis);
        if(finder(i)){
            ans++;
        }
    }
    return ans;
}
int linker2[505];
bool finder2(int num){
    if(num==-1){
        return true;
    }
    if(drugged[num]){
        return false;
    }
    for(int i=egs[num];i!=-1;i=edges[i].nxt){
        int nxt=edges[i].to;
        if(!vis[nxt]){
            vis[nxt]=true;
            if(finder2(linker2[nxt])){
                linker2[nxt]=num;
                return true;
            }
        }
    }
    return false;
}
int hungary2(){
    int ans=0;
    memset(linker2,-1,sizeof linker2);
    for(int i=1;i<=n;i++){
        if(linker[i]!=-1){
            drugged[linker[i]]=true;
        }
    }
    for(int i=1;i<=m;i++){
        if(drugged[i]){
            continue;
        }
        if(k==0){
            return ans;
        }
        memset(vis,0,sizeof vis);
        if(finder2(i)){
            ans++;
            k--;
        }
    }
    return ans;
}
void solve(){
    memset(egs,-1,sizeof egs);
    edges.clear();
    memset(drugged,0,sizeof drugged);
    scanf("%d%d",&m,&k);
    for (int i=1;i<=n;i++){
        int kk;
        scanf("%d",&kk);
        for (int j=0;j<kk;j++){
            int a;
            scanf("%d",&a);
            addedge(a,i);
        }
    }
    int ans=hungary();
    ans+=hungary2();
    printf("%d\n",ans);
}
int main(){
    //freopen("input.in","r",stdin);
	while (scanf("%d",&n)!=EOF)
		solve();
	return 0;
}

代码(网络流)

有大佬说会卡dinic,连夜学的ISAP,把网络流的板子顺便补了

#include <iostream>
#include <vector>
#include <cstring>
#include <queue>
using namespace std;
int n,m,k;
struct edge {
    int from;
    int to;
    int flow;
    int cap;
    int nxt;
    edge(int f, int t, int c, int n) : from(f), to(t), cap(c), nxt(n) {
        flow = 0;
    }
};
vector<edge> edges;
int egs[1005];
void addedge(int f,int t,int c) {
    edges.emplace_back(f, t, c, egs[f]);
    egs[f] = edges.size() - 1;
    edges.emplace_back(t, f, 0, egs[t]);
    egs[t] = edges.size() - 1;
}
int dis[1005];
bool vis[1005];
bool bfs(int s,int t) {
    memset(vis, 0, sizeof vis);
    memset(dis, 0, sizeof dis);
    queue<int> qq;
    qq.push(t);
    vis[t] = true;
    dis[t] = 0;
    while (!qq.empty()) {
        int now = qq.front();
        qq.pop();
        for (int i = egs[now]; i != -1; i = edges[i].nxt) {
            if (!vis[edges[i].to] && edges[i].cap - edges[i].flow > 0) {
                dis[edges[i].to] = dis[now] + 1;
                vis[edges[i].to] = true;
                qq.push(edges[i].to);
            }

        }

    }
    if (dis[t] != 0) {
        return true;
    } else {
        return false;
    }
}
int pre[1005];
int aug(int s,int t) {
    int now = t;
    int greencap = 0x3f3f3f3f;
    while (now != s) {
        greencap = min(greencap, edges[pre[now]].cap - edges[pre[now]].flow);
        now = edges[pre[now]].from;
    }
    now = t;
    while (now != s) {
        edges[pre[now]].flow += greencap;
        edges[pre[now] ^ 1].flow -= greencap;
        now = edges[pre[now]].from;
    }
    return greencap;
}
int num[1005];
int cur[1005];
int isap(int s,int t) {
    bfs(s, t);
    memset(num, 0, sizeof num);
    for (int i = 0; i <= n + m + 2; i++) {
        cur[i] = egs[i];
        num[dis[i]]++;
    }
    int now = s;
    int maxflow = 0;
    while (dis[s] < n + m + 2) {
        if (now == t) {
            maxflow += aug(s, t);
            now = s;
        }
        bool isoperated = false;
        for (int i = cur[now]; i != -1; i = edges[i].nxt) {
            if (edges[i].flow < edges[i].cap && dis[edges[i].from] == dis[edges[i].to] + 1) {
                cur[now] = i;
                pre[edges[i].to] = i;
                now = edges[i].to;
                isoperated = true;
                break;
            }
        }
        if (!isoperated) {
            int miner = n + m + 2;
            for (int i = egs[now]; i != -1; i = edges[i].nxt) {
                if (edges[i].flow < edges[i].cap) {
                    miner = min(miner, dis[edges[i].to] + 1);
                }
            }
            if (--num[dis[now]] < 0) {
                break;
            }
            dis[now] = miner;
            num[dis[now]]++;
            cur[now] = egs[now];
            if (now != s)now = edges[pre[now]].from;
        }
    }
    return maxflow;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    while(cin>>n>>m>>k) {
        memset(egs, -1, sizeof egs);
        memset(pre,-1,sizeof pre);
        edges.clear();
        for(int i=1;i<=n;i++) {
            int t;
            cin>>t;
            for (int j = 0; j < t; j++) {
                int a;
                cin >> a ;
                addedge(i, a+n, 1);
            }
        }
        addedge(0,n+m+2,k);
        for(int i=1;i<=n;i++) {
            addedge(0, i, 1);
            addedge(n + m + 2, i, 1);
        }
        for(int i=1;i<=m;i++) {
            addedge(i + n, n + m + 1, 1);
        }
        cout<<isap(0,n + m + 1)<<endl;
    }
    return 0;
}

参考了以下博主,在此表示感谢:
https://www.itread01.com/content/1543472766.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值