作者:东林AotoriChiaki
链接:https://www.nowcoder.com/discuss/90015?type=101&order=0&pos=1&page=1
来源:牛客网
题意:有n个宿舍,每个宿舍住4个人,给定去年n个宿舍的人员构成,今年n个4人组合的情况,求最少几个人需要搬寝室。
思路:转化为最小费用最大流解决的二分图问题,对每个去年的宿舍,向每个今年的组合连一条边,权值为1费用为需要搬的人数(4-相同的人数),源点到去年各点,今年各点到汇点,都连一条权值为1费用为0的最大流,跑一次费用流即可。
mycode:
自己踩的坑:book数组没有初始化
费用流:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#include<vector>
#include<iostream>
using namespace std;
struct node
{
int u, to, w, c, next;//当前点,目的点,流量,费用,另一个目的点对应Map[]下标
};
#define inf 0x3f3f3f3f
node Map[162000];
int head[160500], vis[500], dist[500], pre[500], cnt;
void addedge(int u, int v, int w, int c)//前向星存图
{
Map[cnt].u = u;
Map[cnt].to = v;
Map[cnt].w = w;//正向为w
Map[cnt].c = c;//正向为+
Map[cnt].next = head[u];
head[u] = cnt++;
Map[cnt].u = v;
Map[cnt].to = u;
Map[cnt].w = 0;//反向为0
Map[cnt].c = -c;//反向为-
Map[cnt].next = head[v];
head[v] = cnt++;
}
int spfa(int s, int e)//求最短路,记录路径
{
memset(dist, inf, sizeof(dist));
memset(vis, 0, sizeof(vis));
memset(pre, -1, sizeof(pre));
queue<int> q;
q.push(s);
dist[s] = 0;
while(!q.empty())
{
s = q.front();
q.pop();
vis[s] = 0;
for(int i = head[s]; ~i; i = Map[i].next)
{
int to = Map[i].to, w = Map[i].w, c = Map[i].c;
if(dist[to] > dist[s] + c && w)
{
dist[to] = dist[s] + c;
pre[to] = i;
if(!vis[to])
{
vis[to] = 1;
q.push(to);
}
}
}
}
if(pre[e] != -1) return 1;
else return 0;
}
int Min_cost(int s, int e)
{
int ans = 0;
while(spfa(s, e))//能找到最短路,也就是增广路
{
int max_flow = inf;
int u = pre[e];
while(u != -1)
{
max_flow = min(max_flow, Map[u].w);//这条增广路,限制流量
u = pre[Map[u].u];
}
u = pre[e];
while(u != -1)//更新边,和最小费用
{
Map[u].w -= max_flow;
Map[u^1].w += max_flow;
ans += max_flow * Map[u].c;
u = pre[Map[u].u];
}
}
return ans;
}
vector<int>da[500],da2[600];
int main()
{
int n;
memset(head,-1,sizeof head);
cnt= 0;
cin>>n;
int a,b,c,d;
for(int i=1; i<=n; i++)
{
cin>>a>>b>>c>>d;
da[i].push_back(a);
da[i].push_back(b);
da[i].push_back(c);
da[i].push_back(d);
//sort(da[i].begin(),da[i].end());
}
for(int i=1; i<=n; i++)
{
cin>>a>>b>>c>>d;
da2[i].push_back(a);
da2[i].push_back(b);
da2[i].push_back(c);
da2[i].push_back(d);
//sort(da2[i].begin(),da2[i].end());
}
int S =0,E = n+n+10;
for(int i=1; i<=n; i++)
{
addedge(S,i,1,0);
}
for(int i=1; i<=n; i++)
{
addedge(i+n,E,1,0);
}
for(int i=1; i<=n; i++)
{
for(int p=1; p<=n; p++)
{
int cnt =0 ;
for(int j=0; j<4; j++)
{
for(int k=0; k<4; k++)
{
if(da[i][j]==da2[p][k])
{
cnt++;
break;
}
}
}
// cout<<cnt<<endl;
addedge(i,p+n,1,4-cnt);
}
}
int ans = Min_cost(S,E);
cout<<ans<<endl;
return 0;
}
KM算法:
#include<bits/stdc++.h>
using namespace std;
/* KM 算法
* 复杂度 O(nx*nx*ny)
* 求最大权匹配
* 若求最小权匹配,可将权值取相反数,结果取相反数
* 点的编号从 0 开始
*/
const int N = 3010;
const int INF = 0x3f3f3f3f;
int nx,ny;//两边的点数
int g[N][N];//二分图描述
int linker[N],lx[N],ly[N];//y 中各点匹配状态,x,y 中的点标号
int 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;
int 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;
}
int 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;
int 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;
}
}
}
int res = 0;
for(int i = 0; i < ny; i++)
if(linker[i] != -1)
res += g[linker[i]][i];
return res;
}
HDU 2255
//int main()
//{
// int n;
// while(scanf("%d",&n) == 1)
// {
// for(int i = 0; i < n; i++)
// for(int j = 0; j < n; j++)
// scanf("%d",&g[i][j]);
// nx = ny = n;
// printf("%d\n",KM());
// }
// return 0;
//}
vector<int>da[500],da2[600];
int main()
{
int n;
memset(visx,0,sizeof visx);
memset(visy,0,sizeof visy);
cin>>n;
int a,b,c,d;
for(int i=0; i<n; i++)
{
cin>>a>>b>>c>>d;
da[i].push_back(a);
da[i].push_back(b);
da[i].push_back(c);
da[i].push_back(d);
//sort(da[i].begin(),da[i].end());
}
for(int i=0; i<n; i++)
{
cin>>a>>b>>c>>d;
da2[i].push_back(a);
da2[i].push_back(b);
da2[i].push_back(c);
da2[i].push_back(d);
//sort(da2[i].begin(),da2[i].end());
}
for(int i=0; i<n; i++)
{
for(int p=0; p<n; p++)
{
int cnt =0 ;
for(int j=0; j<4; j++)
{
for(int k=0; k<4; k++)
{
if(da[i][j]==da2[p][k])
{
cnt++;
break;
}
}
g[i][p] = cnt;
//ag[p][i] = 4-cnt;
}
// cout<<cnt<<endl;
}
}
nx =ny =n;
int ans = KM();
cout<<4*n-ans<<endl;
return 0;
}