标签:
题目链接:7530
题目大意:一个公司有n个开发者,有m个APP可开发。其中一些开发者必选,一些APP必选。已知每个开发者开发每个APP的收益,求最大收益。(每个开发者最多开发一个APP,每个APP最多一个人开发)
题目思路:
解法一:二分图最佳匹配(KM算法)
增加一些虚开发者和虚app,非必要app可以被虚开发者开发,收益为0,反过来非必要开发者可以开发虚app,收益为0。然后虚开发者也可以开发虚app,收益为0,这样的设定可以使得km的构图条件能满足。
注意:必要的开发者和其对应不可开发的app直接设置为一个负的INF2值,使得他们之间有边可走但是如果真的要选择这条边最后答案得出来肯定是负数。也就是不存在可行方案。同理必要的app和其对应不可的开发者
权值的设置:
if (存在一个重要的app)边权值加INF
if (存在一个重要的开发者)边权值加INF
//最多可加两个INF
然后套用模板即可。
以下是代码:
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <functional>
#include <numeric>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <vector>
#include <queue>
#include <deque>
#include <list>
using namespace std;
#define ll long long
#define MOD 1000000000
const int N = 105;
const ll INF = 100000000000000LL;
const ll INF2 = 1000000000000LL;
int nx,ny;//两边的点数 nx,第一队的个数,ny,第二队的个数。注意 nx <= ny,否则会死循环。
ll g[N][N];//二分图描述 g[i][j],i属于第一队,j属于第二队。
ll linker[N],lx[N],ly[N];//y中各点匹配状态,x,y中的点标号
ll slack[N];
bool visx[N],visy[N];
bool DFS(int x)
{
visx[x] = true;
for(int y = 0; y < ny; y++)
{
if(visy[y])continue;
ll tmp = lx[x] + ly[y] - g[x][y];
if(tmp == 0)
{
visy[y] = true;
if(linker[y] == -1 || DFS(linker[y]))
{
linker[y] = x;
return true;
}
}
else if(slack[y] > tmp)
slack[y] = tmp;
}
return false;
}
ll KM()
{
memset(linker,-1,sizeof(linker));
memset(ly,0,sizeof(ly));
for(int i = 0;i < nx;i++)
{
lx[i] = -INF;
for(int j = 0;j < ny;j++)
if(g[i][j] > lx[i])
lx[i] = g[i][j];
}
for(int x = 0;x < nx;x++)
{
for(int i = 0;i < ny;i++)
slack[i] = INF;
while(true)
{
memset(visx,false,sizeof(visx));
memset(visy,false,sizeof(visy));
if(DFS(x))break;
ll d = INF;
for(int i = 0;i < ny;i++)
if(!visy[i] && d > slack[i])
d = slack[i];
for(int i = 0;i < nx;i++)
if(visx[i])
lx[i] -= d;
for(int i = 0;i < ny;i++)
{
if(visy[i])ly[i] += d;
else slack[i] -= d;
}
}
}
ll res = 0;
for(int i = 0;i < ny;i++)
if(linker[i] != -1)
res += g[linker[i]][i];
return res;
}
int imp[105];
int imb[105];
int main()
{
int n,m;
while(cin >> n >> m)
{
if(n == 0 && m == 0) break;
memset(imp,0,sizeof(imp));
memset(imb,0,sizeof(imb));
memset(g,0,sizeof(g));
nx = ny = max(n,m);
int t,x,a,b;
cin >> t;
while(t--)
{
cin >> x;
imp[x-1] = 1;
for (int i = 0; i < nx; i++)
{
g[x-1][i] = -INF2;
/*不能设置为INF,因为设置为INF就是没有点相连,
但是设置为INF2(小于INF)表示这条边可以连,如果你一定要连,连出来是负值,则无答案*/
}
}
cin >> t;
while(t--)
{
cin >> x;
imb[x-1] = 1;
for (int i = 0; i < nx; i++)
{
g[i][x-1] = -INF2;
}
}
for (int i = 0; i < n; i++)
{
cin >> t;
while(t--)
{
cin >> a >> b;
g[i][a - 1] = b;
if (imp[i]) g[i][a - 1] += MOD;
if (imb[a - 1]) g[i][a - 1] += MOD;
}
}
ll ans = KM();
if (ans < 0) cout << -1 << endl;
else cout << ans % MOD << endl;
}
}
/*
2 4
1 1
1 3
2 1 8 2 10
3 2 2 3 10 4 50
4 3
3 1 2 4
2 1 3
1 1 200
2 2 700 3 200
2 2 300 3 100
1 1 500
*/
解法二:上下界最大费用可行流
虚设一个起点和终点,如图所示,容量都为1,如果存在必做的app或开发者,这条边权值设置为INF。
以下是代码:
待补
Regionals 2015 >> Asia - Tehran >> 7530 - Cafebazaar【二分图最佳匹配】【KM】【最大费用流】
标签:
原文地址:http://blog.csdn.net/loy_184548/article/details/52097652