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 on test 7,日后排序再试试,反正比赛的时候我们这个算法怎么也找不到反例,自闭了
#include
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 edges;
vector 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
有大佬说会卡dinic,连夜学的ISAP,把网络流的板子顺便补了
#include
#include
#include
#include
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 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 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<
参考了以下博主,在此表示感谢:
https://www.itread01.com/content/1543472766.html