hdu2255(Chocolate,km算法+构图)

https://vjudge.net/problem/HDU-2282

题意:

有n个盒子围成一圈,第ii个盒子里有a[i]个糖,总糖数不超过n,一次操作可以把一个盒子里的一个糖放到相邻的盒子里,问至少需要多少次操作可以让每个盒子里至多一个糖

解析:

糖果数目>1,将a[i]-1放入其他a[i]==0的盒子中,记录代价,跑km算法,这里求最小,建负边,得到的最大值然后反转就为正的最小值

代价为min(abs(i-k),n-abs(i-k))

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define inf 9999999

using namespace std;
const int maxn=500+5;

int w[maxn][maxn];
int match[maxn],slack[maxn];
int vis_x[maxn],vis_y[maxn];
int ex[maxn],ey[maxn];
int nx,n;

int dfs(int x)
{
    vis_x[x]=1;
    for(int y=0;y<n;y++)
    {
        if(vis_y[y]) continue;
        int gap=ex[x]+ey[y]-w[x][y];
        if(!gap)
        {
            vis_y[y]=1;
            if(match[y]==-1||dfs(match[y]))
            {
                match[y]=x;
                return 1;
            }
        }
        else slack[y]=min(gap,slack[y]);
    }
    return 0;
}


void km()
{
    memset(match,-1,sizeof(match));
    memset(ey,0,sizeof(ey));
    for(int i=0;i<nx;i++)
    {
        ex[i]=-inf;
        for(int j=0;j<n;j++)
            ex[i]=max(w[i][j],ex[i]);
    }
    for(int i=0;i<nx;i++)
    {
        fill(slack,slack+n+1,inf);
        while(1)
        {
            memset(vis_x,0,sizeof(vis_x));
            memset(vis_y,0,sizeof(vis_y));
            if(dfs(i)) break;
            int d=inf;
            for(int j=0;j<n;j++)
                if(!vis_y[j]) d=min(slack[j],d);
            for(int j=0;j<nx;j++)
                if(vis_x[j]) ex[j]-=d;
            for(int j=0;j<n;j++)
                if(vis_y[j]) ey[j]+=d;
            else slack[j]-=d;
        }
    }
    int res=0;
    for(int i=0;i<n;i++)
        if(match[i]!=-1) res+=w[match[i]][i];
    printf("%d\n",-res);
}

int main()
{
    int a[maxn];
    while(scanf("%d",&n)!=EOF){
        nx=0;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                w[i][j]=-inf;
        for(int i=0;i<n;i++)
            scanf("%d",&a[i]);
        for(int i=0;i<n;i++){
            if(a[i]>1)//a[i]-1给别的盒子
            {
                for(int j=1;j<a[i];j++){
                    for(int k=0;k<n;k++)
                        if(a[k]==0)
                            w[nx][k]=-min(abs(i-k),n-abs(i-k));
                    nx++;
                }
            }
        }
        km();
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值