HDU 2426 Interesting Housing Problem 二分匹配(KM模板)或者最小费用最大流

本文深入剖析了当前信息技术领域的核心挑战与创新解决方案,涵盖了从基础到应用的多个层面,包括但不限于开发工具、大数据处理、AI音视频处理、测试与自动化运维等。通过详细解析这些技术领域的发展趋势与实践案例,旨在为读者提供全面的技术洞察与应用指南。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

点击打开链接

Interesting Housing Problem

Time Limit: 10000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2151    Accepted Submission(s): 798


Problem Description
For any school, it is hard to find a feasible accommodation plan with every student assigned to a suitable apartment while keeping everyone happy, let alone an optimal one. Recently the president of University ABC, Peterson, is facing a similar problem. While Peterson does not like the idea of delegating the task directly to the class advisors as so many other schools are doing, he still wants to design a creative plan such that no student is assigned to a room he/she dislikes, and the overall quality of the plan should be maximized. Nevertheless, Peterson does not know how this task could be accomplished, so he asks you to solve this so-called "interesting" problem for him.
Suppose that there are N students and M rooms. Each student is asked to rate some rooms (not necessarily all M rooms) by stating how he/she likes the room. The rating can be represented as an integer, positive value meaning that the student consider the room to be of good quality, zero indicating neutral, or negative implying that the student does not like living in the room. Note that you can never assign a student to a room which he/she has not rated, as the absence of rating indicates that the student cannot live in the room for other reasons.
With limited information available, you've decided to simply find an assignment such that every student is assigned to a room he/she has rated, no two students are assigned to the same room, and the sum of rating is maximized while satisfying Peterson's requirement. The question is … what exactly is the answer?
 

Input
There are multiple test cases in the input file. Each test case begins with three integers, N, M, and E (1 <= N <= 500, 0 <= M <= 500, 0 <= E <= min(N * M, 50000)), followed by E lines, each line containing three numbers, S i, R i, V i, (0 <= S i < N, 0 <= R i < M, |V i| <= 10000), describing the rating V i given by student S i for room R i. It is guaranteed that each student will rate each room at most once.
Each case is followed by one blank line. Input ends with End-of-File.
 

Output
For each test case, please output one integer, the requested value, on a single line, or -1 if no solution could be found. Use the format as indicated in the sample output.
 

Sample Input
  
3 5 5 0 1 5 0 2 7 1 1 6 1 2 3 2 4 5 1 1 1 0 0 0 1 1 0
 

Sample Output
  
Case 1: 18 Case 2: 0 Case 3: -1
 

Source
 

Recommend
lcy
 
 
题意:一个学校里面有n个学生,m个宿舍,学生对某一宿舍有一个r值,代表该学生对该宿舍的满意度,r>0说明满意,r=0,说明不喜欢也不讨厌,r<0,说明讨厌。每个宿舍只住一个学生,校长应该怎么分配学生才能使得满意度达到最大?
用二分匹配来做比较简单,直接套KM模板就行。如果最小费用最大流的话,关键在于建图,建一个超级源点与学生相连,超级汇点与宿舍相连,学生和宿舍之间也有联系。但是,我用最小费用最大流来做此题,结果是TLE,可能有些地方没有优化好,不过,我知道,用最小费用最大流的思想是可以AC此题的。
 
//二分匹配
#include<stdio.h>
#include<string.h>
#define inf 99999
using namespace std;
int g[507][507];
int lx[507],ly[507];
int slack[507],match[507];
bool visx[507],visy[507];
int n,m,t;

bool dfs(int cur)
{
    visx[cur]=true;
    for(int y=1;y<=m;y++)
    {
        if(visy[y])continue;
        int t=lx[cur]+ly[y]-g[cur][y];
        if(t==0)
        {
            visy[y]=true;
            if(match[y]==-1||dfs(match[y]))
            {
                match[y]=cur;
                return true;
            }
        }
        else if(slack[y]>t)
        {
            slack[y]=t;
        }
    }
    return false;
}

int KM()
{
    memset(match,-1,sizeof(match));
    memset(ly,0,sizeof(ly));
    for(int i=1;i<=n;i++)
    {
        lx[i]=-inf;
        for(int j=1;j<=m;j++)
        {
            if(g[i][j]>lx[i])
            lx[i]=g[i][j];
        }
    }
    for(int x=1;x<=n;x++)
    {
        for(int i=1;i<=m;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=1;i<=m;i++)
            {
                if(!visy[i]&&d>slack[i])
                d=slack[i];
            }
            for(int i=1;i<=n;i++)
            if(visx[i])
            {
                lx[i]-=d;
            }
            for(int i=1;i<=m;i++)
            if(visy[i])ly[i]+=d;
            else slack[i]-=d;
        }
    }
    int reslut=0,flag=0;
    for(int i=1;i<=m;i++)
    {
        if(match[i]==-1||g[match[i]][i]==-inf)
        continue;
        if(match[i]>-1)
        {
            reslut+=g[match[i]][i];
            flag++;
        }
    }
    if(flag<n)reslut=-1;
    return reslut;
}
int main()
{
    int cas=1;
    while(scanf("%d%d%d",&n,&m,&t)!=EOF)
    {
        memset(g,0,sizeof(g));
        for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        g[i][j]=-inf;
        int a,b,c;
        for(int i=1;i<=t;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            a++;b++;
            if(c<0)continue;
            g[a][b]=c;
        }
        int ans=KM();
        printf("Case %d: %d\n",cas++,ans);
    }
    return 0;
}

 
//TLE的最小费用最大流
#include <iostream>
#include <algorithm>
#include <string>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <memory.h>
#include <queue>
#include <vector>
#include <cmath>
using namespace std;
int sumFlow;
const int MAXN = 507;
const int INF = 1000000000;
struct Edge
{
    int u, v, cap, cost;
    int next;
} edge[250000];
int NE,N,M,E;
int head[MAXN], dist[MAXN], pp[MAXN];
bool vis[MAXN];
void init()
{
    NE = 0;
    memset(head, -1, sizeof(head));
}
void addedge(int u, int v, int cap, int cost)
{
    edge[NE].u = u;
    edge[NE].v = v;
    edge[NE].cap = cap;
    edge[NE].cost = cost;
    edge[NE].next = head[u];
    head[u] = NE++;
    edge[NE].u = v;
    edge[NE].v = u;
    edge[NE].cap = 0;
    edge[NE].cost = -cost;
    edge[NE].next = head[v];
    head[v] = NE++;
}
bool SPFA(int s, int t, int n)
{
    int i, u, v;
    queue <int> qu;
    memset(vis,false,sizeof(vis));
    memset(pp,-1,sizeof(pp));
    for(i = 0; i <= n; i++) dist[i] = INF;
    vis[s] = true;
    dist[s] = 0;
    qu.push(s);
    while(!qu.empty())
    {
        u = qu.front();
        qu.pop();
        vis[u] = false;
        for(i = head[u]; i != -1; i = edge[i].next)
        {
            v = edge[i].v;
            if(edge[i].cap && dist[v] > dist[u] + edge[i].cost)
            {
                dist[v] = dist[u] + edge[i].cost;
                pp[v] = i;
                if(!vis[v])
                {
                    qu.push(v);
                    vis[v] = true;
                }
            }
        }
    }
    if(dist[t] == INF) return false;
    return true;
}
int MCMF(int s, int t, int n) // minCostMaxFlow
{
    int flow = 0; // 总流量
    int i, minflow, mincost;
    mincost = 0;
    while(SPFA(s, t, n))
    {
        minflow = INF + 1;
        for(i = pp[t]; i != -1; i = pp[edge[i].u])
            if(edge[i].cap < minflow)
                minflow = edge[i].cap;
        flow += minflow;
        for(i = pp[t]; i != -1; i = pp[edge[i].u])
        {
            edge[i].cap -= minflow;
            edge[i^1].cap += minflow;
        }
        mincost += dist[t] * minflow;
    }
    sumFlow = flow; // 最大流
    if(sumFlow!=N) return 1;
    return mincost;
}
int main()
{
    int cas=1;
    while(scanf("%d%d%d",&N,&M,&E)!=EOF)
    {
        int u, v, c;
        int s,r,vv;
        init();
        int S=0;//S是源点
        int T=N+M+1;//T是汇点
        for(int i=0;i<E;i++)
        {
            scanf("%d%d%d",&s,&r,&vv);
            s++,r++;
            if(v>=0)
            {
                addedge(s,N+r,1,-vv);
            }
        }
        for(int i=1;i<=N;i++)
        {
            addedge(0,i,1,0);
        }
        for(int i=1;i<=M;i++)
        addedge(N+i,T,1,0);
        int ans =- MCMF(S, T, T+1);//T+1是点的个数
        printf("Case %d: %d\n", cas++,ans);
    }


    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值