HDU 2426 Interesting Housing Problem 最小费用最大流 or KM算法

2023-11-08 09:32

本文主要是介绍HDU 2426 Interesting Housing Problem 最小费用最大流 or KM算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这道题貌似是08年杭州网络赛的题目, 看完题后就发现是个最优匹配问题,用KM或者最小费用最大流做, 就找了个最小费用最大流的模板,改了一下,就能过了、

建图还是比较好想的,建立一个超级源点,一个超级汇点,然后源点与学生连边,流量限制为1,费用为0,汇点与房间连边,流量限制为1, 费用为0, 然后学生与房间之间的边,流量限制是1,费用就是题目给的喜好值了, 注意,每次加边,都要加一个相应的反向边, 就是流量为0, 费用为正向边的负数。

然后题目中要求不能把学生分到不喜欢的房间里,所以碰见喜好值是负数就不管了。 另外,由于是最小费用么,所以所有喜好值都是乘以-1后加进去的,这样求出的最小值,输出的时候再乘以-1就变成最大的了。

最后判断能不能所有学生都分配到房间里, 就判断流量的大小是否等于学生个数就行了


/*
ID: sdj22251
PROG: subset
LANG: C++
*/
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define MAXN 100005
#define eps 1e-11
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
int tot = 0, x, y;
class mincost
{
private:const static int V = 2001; //注意点的个数, 应该为学生+房间+2, 所以至少要开到1002const static int E = 2000001;const static int INF = -1u >> 1;struct Edge{int v, cap, cost;Edge *next;} pool[E], *g[V], *pp, *pree[V];int T, S, dis[V], pre[V];int n, m, flow, cirq[V];void SPFA();inline void addedge(int i, int j, int cap, int cost);
public:bool initialize(int x, int y, int z);void mincost_maxflow();
};void mincost::mincost_maxflow()
{while (true){SPFA();if (dis[T] == INF)break;int minn = INF;for (int i = T; i != S; i = pre[i])minn = min(minn, pree[i]->cap);for (int i = T; i != S; i = pre[i]){pree[i]->cap -= minn;pool[(pree[i] - pool)^0x1].cap += minn;flow += minn * pree[i]->cost;}tot += minn;  //流量计算}if(tot != x) flow = 1;printf("%d\n", -flow);
}void mincost::SPFA()
{bool vst[V] = {false};int tail = 0, u;fill(dis,dis + n,0x7fffffff);cirq[0] = S;vst[S] = 1;dis[S] = 0;for (int i = 0; i <= tail; i++){int v = cirq[i % n];for (Edge *i = g[v]; i != NULL; i = i->next){if (!i->cap)continue;u = i->v;if (i->cost + dis[v] < dis[u]){dis[u] = i->cost + dis[v];pree[u] = i;pre[u] = v;if (!vst[u]){tail++;cirq[tail % n] = u;vst[u] = true;}}}vst[v] = false;}
}void mincost::addedge(int i, int j, int cap, int cost)
{pp->cap = cap;pp->v = j;pp->cost = cost;pp->next = g[i];g[i] = pp++;
}
bool mincost::initialize(int x, int y, int z)
{memset(g, 0, sizeof (g));pp = pool;n = x + y + 2;  //n即为顶点的个数   学生数+房间数+一个源点+一个汇点m = z;S = 0;T = x + y + 1;int v, u, f, c;for (int i = 0; i < m; i++){scanf("%d %d %d", &v, &u, &c);v++;u++;if(c >= 0){addedge(v, u + x, 1, -c);addedge(u + x, v, 0, c);}}for(int i = 1; i <= x; i++){addedge(0, i, 1, 0);addedge(i, 0, 0, 0);}for(int i = 1; i <= y; i++){addedge(x + i, T, 1, 0);addedge(T, x + i, 0, 0);}flow = 0;return true;
}
mincost g;
int main()
{//freopen("d:/data.in","r",stdin);//freopen("d:/data.out","w",stdout);int  z, cas = 0;while(scanf("%d%d%d", &x, &y, &z) != EOF){g.initialize(x, y, z);printf("Case %d: ", ++cas);tot = 0;g.mincost_maxflow();}return 0;
}

接下来是KM算法的 最后判断是否完备匹配就行了


#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define MAXN 505
#define MAXM 555555
#define INF 1000000000
using namespace std;
int n, m, ny, nx;
int w[MAXN][MAXN];
int lx[MAXN], ly[MAXN];
int linky[MAXN];
int visx[MAXN], visy[MAXN];
int slack[MAXN];
bool find(int x)
{visx[x] = 1;for(int y = 1; y <= ny; y++){if(visy[y]) continue;int t = lx[x] + ly[y] - w[x][y];if(t == 0){visy[y] = 1;if(linky[y] == -1 || find(linky[y])){linky[y] = x;return true;}}else if(slack[y] > t) slack[y] = t;}return false;
}
void KM()
{memset(linky, -1, sizeof(linky));for(int i = 1; i <= nx; i++) lx[i] = -INF;memset(ly, 0, sizeof(ly));for(int i = 1; i <= nx; i++)for(int j = 1; j <= ny; j++)if(w[i][j] > lx[i]) lx[i] = w[i][j];for(int x = 1; x <= nx; x++){for(int i = 1; i <= ny; i++) slack[i] = INF;while(true){memset(visx, 0, sizeof(visx));memset(visy, 0, sizeof(visy));if(find(x)) break;int d = INF;for(int i = 1; i <= ny; i++)if(!visy[i]) d = min(d, slack[i]);for(int i = 1; i <= nx; i++)if(visx[i]) lx[i] -=d;for(int i = 1; i <= ny; i++)if(visy[i]) ly[i] += d;else slack[i] -= d;}}
}
int main()
{int x, y, z, e, cas = 0;while(scanf("%d%d%d", &n, &m, &e) != EOF){nx = n, ny = m;for(int i = 1; i <= n; i++)for(int j = 1; j <= m; j++)w[i][j] = -INF;while(e--){scanf("%d%d%d", &x, &y, &z);if(z >= 0) w[x + 1][y + 1] = z;}printf("Case %d: ", ++cas);KM();int cnt = 0, ans = 0;for(int i = 1; i <= ny; i++)if(linky[i] != -1 && w[linky[i]][i] != -INF)ans += w[linky[i]][i], cnt++;if(cnt != nx) ans = -1;printf("%d\n", ans);}return 0;
}


这篇关于HDU 2426 Interesting Housing Problem 最小费用最大流 or KM算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/369026

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

hdu 2093 考试排名(sscanf)

模拟题。 直接从教程里拉解析。 因为表格里的数据格式不统一。有时候有"()",有时候又没有。而它也不会给我们提示。 这种情况下,就只能它它们统一看作字符串来处理了。现在就请出我们的主角sscanf()! sscanf 语法: #include int sscanf( const char *buffer, const char *format, ... ); 函数sscanf()和

hdu 2602 and poj 3624(01背包)

01背包的模板题。 hdu2602代码: #include<stdio.h>#include<string.h>const int MaxN = 1001;int max(int a, int b){return a > b ? a : b;}int w[MaxN];int v[MaxN];int dp[MaxN];int main(){int T;int N, V;s

poj 1258 Agri-Net(最小生成树模板代码)

感觉用这题来当模板更适合。 题意就是给你邻接矩阵求最小生成树啦。~ prim代码:效率很高。172k...0ms。 #include<stdio.h>#include<algorithm>using namespace std;const int MaxN = 101;const int INF = 0x3f3f3f3f;int g[MaxN][MaxN];int n