作者:東林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;
}