牛客国庆集训派对Day6 A-Birthday (最小费用流)

版权声明:Why is everything so heavy? https://blog.csdn.net/lzc504603913/article/details/82953130

链接:https://www.nowcoder.com/acm/contest/206/A
来源:牛客网
 

题目描述

恬恬的生日临近了。宇扬给她准备了一个蛋糕。
正如往常一样,宇扬在蛋糕上插了n支蜡烛,并把蛋糕分为m个区域。因为某种原因,他必须把第i根蜡烛插在第ai个区域或第bi个区域。区域之间是不相交的。宇扬在一个区域内同时摆放x支蜡烛就要花费x2的时间。宇扬布置蛋糕所用的总时间是他在每个区域花的时间的和。
宇扬想快些见到恬恬,你能告诉他布置蛋糕最少需要多少时间吗?

输入描述:

第一行包含两个整数n,m(1 ≤ n ≤ 50, 2≤ m≤ 50)。
接下来n行,每行两个整数ai,bi(1 ≤ ai, bi ≤ m)。

输出描述:

一个整数表示答案。

示例1

输入

复制

3 3
1 2
1 2
1 2

输出

复制

5

示例2

输入

复制

3 3
1 2
2 3
1 3

输出

复制

3

解题思路:最直接的思路,暴力搜索,枚举2^50种情况。加上剪枝可过(记录最小的答案)。另外一种思路,要使的和最小,肯定是要使数字尽可能地平均,因此用优先队列维护每一个数字,然后不断地尝试把最大的那个数字移到其他数字里去,看看答案会不会减小,然后更新答案。

扫描二维码关注公众号,回复: 3465022 查看本文章

最好的思路是费用流。

每个蜡烛建一个点。每个区域建一个点

建立一个超级源点连向每一个蜡烛,流量为1,费用为0.

建立一个超级汇点,每一个区域向汇点连边,记为边集E,流量为INF,费用初始为1.

每个蜡烛向他的区域连边,流量为1,费用为0.

我们比较关心边集E,这里的边流量每+1,他的费用都要更新,因此我们每次SPFA后,都要判断增广路径是否经过这个边集E,然后更新这条边的费用,反向边也要更新。

如果不想更新费用,可以对于每一个区域再新建50个节点,每个节点的费用为平方数之差,这样子直接跑就OK。

新建50个的代码

#include <iostream>
#include <string.h>
#include <math.h>
#include <vector>
#include <algorithm>
#include <stdio.h>
#include <queue>
using namespace std;
const int MAXN = 4010;
const int INF = 0x3f3f3f3f;
typedef long long ll;
 
struct edge
{
    int u, v, cap, cost, next;
} e[4 * MAXN];
 
int edge_num;
int head[MAXN];
 
void insert_edge(int u, int v, int cap, int cost)
{
    e[edge_num].u = u;
    e[edge_num].v = v;
    e[edge_num].cap = cap;
    e[edge_num].cost = cost;
    e[edge_num].next = head[u];
    head[u] = edge_num++;
 
    e[edge_num].u = v;
    e[edge_num].v = u;
    e[edge_num].cap = 0;
    //注意这里
    e[edge_num].cost = -cost;
    //注意这里
    e[edge_num].next = head[v];
    head[v] = edge_num++;
 
 
 
}
 
int dis[MAXN];
int pre[MAXN];
bool vis[MAXN];
bool spfa(int s, int t)
{
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    memset(pre, -1, sizeof(pre));
 
    dis[s] = 0;
    vis[s] = 1;
    queue<int> que;
    que.push(s);
    while (!que.empty())
    {
        int tp = que.front();
        que.pop();
        vis[tp] = 0;
        for (int i = head[tp]; ~i; i = e[i].next)
        {
            int v = e[i].v;
            int cost = e[i].cost;
 
            if (e[i].cap && dis[v] > dis[tp] + cost)
            {
                dis[v] = dis[tp] + cost;
                pre[v] = i;
                if (!vis[v])
                {
                    vis[v] = 1;
                    que.push(v);
                }
            }
        }
    }
    if (dis[t] == INF)
        return false;
    return true;
}
 
pair<int, int> MCMF(int s, int t)
{
    int maxflow = 0;
    int mincost = 0;
    int minc;
    while (spfa(s, t))
    {
        minc = INF;
        int cost = 0;
        for (int i = pre[t]; ~i; i = pre[e[i].u])
            minc = min(minc, e[i].cap);
        for (int i = pre[t]; ~i; i = pre[e[i].u])
        {
            e[i].cap -= minc;
            e[i ^ 1].cap += minc;
            cost += minc * e[i].cost; //flow*unit cost=total cost
 
        }
        mincost += cost;
        maxflow += minc;
    }
    return make_pair(mincost, maxflow);
}
 
 
 
int tot = 0;
int in[MAXN];
 
int out[MAXN];
 
int ssout[MAXN][51];
 
 
 
int S, SK, T;
 
 
int main()
{
 
    edge_num = 0;
    memset(head, -1, sizeof(head));
    tot = 0;
 
    int N, M;
    scanf("%d%d", &N, &M);
 
    S = ++tot;
 
    for (int i = 1; i <= N; i++)
        in[i] = ++tot;
 
    for (int i = 1; i <= M; i++)
    {
        out[i] = ++tot;
        for(int j=1;j<=50;j++){
            ssout[i][j]=++tot;
        }
    }
    T = ++tot;
 
    int a,b;
    for(int i=1;i<=N;i++){
        scanf("%d%d",&a,&b);
        insert_edge(in[i],out[b],1,0);
        insert_edge(in[i],out[a],1,0);
    }
 
    for(int i=1;i<=M;i++){
        for(int j=1;j<=50;j++){
            insert_edge(out[i],ssout[i][j],1,j*j-(j-1)*(j-1));
        }
    }
 
 
    for(int i=1;i<=N;i++){
        insert_edge(S,in[i],1,0);
    }
 
    for(int i=1;i<=M;i++){
        for(int j=1;j<=50;j++)
        insert_edge(ssout[i][j],T,1,0);
    }
 
    printf("%d\n", MCMF(S, T).first);
 
 
    return 0;
}

修改边费用的代码

#include <iostream>
#include <string.h>
#include <math.h>
#include <vector>
#include <algorithm>
#include <stdio.h>
#include <queue>
using namespace std;
const int MAXN = 4010;
const int INF = 0x3f3f3f3f;
typedef long long ll;
 
struct edge
{
    int u, v, cap, cost, next;
} e[4 * MAXN];
 
int edge_num;
int head[MAXN];
 
int  insert_edge(int u, int v, int cap, int cost)
{
    e[edge_num].u = u;
    e[edge_num].v = v;
    e[edge_num].cap = cap;
    e[edge_num].cost = cost;
    e[edge_num].next = head[u];
    head[u] = edge_num++;
 
    e[edge_num].u = v;
    e[edge_num].v = u;
    e[edge_num].cap = 0;
    //注意这里
    e[edge_num].cost = -cost;
    //注意这里
    e[edge_num].next = head[v];
    head[v] = edge_num++;
 
    return edge_num-2;
 
}
int wm[5006];
bool im[MAXN];
bool om[MAXN];
int dis[MAXN];
int pre[MAXN];
bool vis[MAXN];
bool spfa(int s, int t)
{
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    memset(pre, -1, sizeof(pre));
 
    dis[s] = 0;
    vis[s] = 1;
    queue<int> que;
    que.push(s);
    while (!que.empty())
    {
        int tp = que.front();
        que.pop();
        vis[tp] = 0;
        for (int i = head[tp]; ~i; i = e[i].next)
        {
            int v = e[i].v;
            int cost = e[i].cost;
 
            if (e[i].cap && dis[v] > dis[tp] + cost)
            {
                dis[v] = dis[tp] + cost;
                pre[v] = i;
                if (!vis[v])
                {
                    vis[v] = 1;
                    que.push(v);
                }
            }
        }
    }
    if (dis[t] == INF)
        return false;
    return true;
}
 
pair<int, int> MCMF(int s, int t)
{
    int maxflow = 0;
    int mincost = 0;
    int minc;
    while (spfa(s, t))
    {
        minc = INF;
        int cost = 0;
        for (int i = pre[t]; ~i; i = pre[e[i].u])
            minc = min(minc, e[i].cap);
        for (int i = pre[t]; ~i; i = pre[e[i].u])
        {
            e[i].cap -= minc;
            e[i ^ 1].cap += minc;
            cost += minc * e[i].cost; //flow*unit cost=total cost
 
            if(im[i]){
                mincost-=(wm[e[i].cost]-1)*(wm[e[i].cost]-1);
                e[i].cost=(wm[e[i].cost]+1)*(wm[e[i].cost]+1);
            }
 
            if(om[i]){
                mincost+=(wm[-e[i].cost]-1)*(wm[-e[i].cost]-1);
                e[i].cost=-(wm[-e[i].cost]+1)*(wm[-e[i].cost]+1);
            }
 
        }
        mincost += cost;
        maxflow += minc;
    }
    return make_pair(mincost, maxflow);
}
 
 
 
int tot = 0;
int in[MAXN];
 
int out[MAXN];
 
 
 
 
int S, SK, T;
 
 
int main()
{
    for(int j=1;j<5000;j++)
        for(int i=1;i<=50;i++)
            if(i*i==j)
                wm[j]=i;
    edge_num = 0;
    memset(head, -1, sizeof(head));
    tot = 0;
 
    int N, M;
    scanf("%d%d", &N, &M);
 
    S = ++tot;
    for (int i = 1; i <= N; i++)
        in[i] = ++tot;
 
    for (int i = 1; i <= M; i++)
    {
        out[i] = ++tot;
    }
    T = ++tot;
 
    int a,b;
    for(int i=1;i<=N;i++){
        scanf("%d%d",&a,&b);
        insert_edge(in[i],out[b],1,0);
        insert_edge(in[i],out[a],1,0);
    }
 
    for(int i=1;i<=M;i++){
        int ee=insert_edge(out[i],T,N,1);
        im[ee]=1;
        om[ee+1]=1;
    }
 
 
    for(int i=1;i<=N;i++){
        insert_edge(S,in[i],1,0);
    }
 
 
    printf("%d\n", MCMF(S, T).first);
 
 
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lzc504603913/article/details/82953130