Chocolate(HDU-2282)

Problem Description

Lethe loves eating chocolates very much. In Lethe's birthday, her good friend echo brings N boxes to her, and makes the boxes on the circle. Furthermore, echo tells Lethe that there are many chocolates in the boxes, but the total number of chocolates doesn't exceed N. Also, echo wants Lethe to displace the chocolates in such a way that in each box remains no more than one chocolate. In one move she can shift one chocolate from current box to its neighboring box. (Each box has two neighboring boxes). Can you tell Lethe the minimum number of move to achieve this goal?

Input

There are multi-cases (The total number of cases won't exceed 20). First line is an integer N(1<=N<=500), the total number of boxes. Then N lines follow, each line includes a number, indicates the number of chocolates in the box.

Output

Output the minimum number of move.

Sample Input

10
1
3
3
0
0
2
0
0
0
0

Sample Output

9

题意:给出 n 个盒子,盒子摆成环,每个盒子中有一定数量的巧克力,且所有盒子内巧克力的总数小于 n,每次移动可以将一块巧克力从第 i 格移动到第 i-1 或 i+1 格,问最少移动几次能将使得每个盒子的巧克力数小于等于 1

思路:类似 Going Home(POJ-2195)要求边权值和最小的完全匹配,不过图是一个环,因此存储距离时要注意取最小值的求法

左点集为要移动的巧克力,右点集为空盒子,若一个巧克力 i 能被移动到 j,那么就在左 i 右 j 连一条权值为两者最小距离的边,由于所有盒子连接成一个环,因此要注意最小距离的求法,从第 i 个盒子移动到第 j 个盒子的最小步数是 min(n-abs(i-j),abs(i-j)),由于要求图的最小权值的完全匹配,因此使用 KM 算法时注意将边的权值存为负数,最后得到的答案取负就是最优匹配

Source Program

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<map>
#define PI acos(-1.0)
#define E 1e-6
#define MOD 16007
#define INF 0x3f3f3f3f
#define N 1001
#define LL long long
using namespace std;
int n,m;
int G[N][N];
int Lx[N],Ly[N];
bool visX[N],visY[N];
int linkX[N],linkY[N];
bool dfs(int x){
    visX[x]=true;
    for(int y=1;y<=m;y++){
        if(!visY[y]){
            int temp=Lx[x]+Ly[y]-G[x][y];
            if(temp==0){
                visY[y]=true;
                if(linkY[y]==-1 || dfs(linkY[y])){
                    linkX[x]=y;
                    linkY[y]=x;
                    return true;
                }
            }
        }
    }
    return false;
}
void update(){
    int minn=INF;
    for(int i=1;i<=n;i++)
        if(visX[i])
            for(int j=1;j<=m;j++)
                if(!visY[j])
                    minn=min(minn,Lx[i]+Ly[j]-G[i][j]);

    for(int i=1;i<=n;i++)
        if(visX[i])
            Lx[i]-=minn;

    for(int i=1;i<=m;i++)
        if(visY[i])
            Ly[i]+=minn;
}
int KM(){
    memset(linkX,-1,sizeof(linkX));
    memset(linkY,-1,sizeof(linkY));
    memset(Lx,0,sizeof(Lx));
    memset(Ly,0,sizeof(Ly));

    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            Lx[i]=max(Lx[i],G[i][j]);

    for(int i=1;i<=n;i++){
        while(true){
            memset(visX,false,sizeof(visX));
            memset(visY,false,sizeof(visY));

            if(dfs(i))
                break;
            else
                update();
        }
    }

    int ans=0;
    for(int i=1;i<=m;i++)
        if(linkY[i]!=-1)
            ans+=G[linkY[i]][i];

    return ans;
}
int chocolate[N],box[N];
int numChocolate,numBox;
int main(){
    while(scanf("%d",&n)!=EOF&&(n)){
        numChocolate=numBox=0;
        memset(chocolate,0,sizeof(chocolate));
        memset(box,0,sizeof(box));

        for(int i=1;i<=n;i++){
            int x;
            scanf("%d",&x);
            if(x>1){
                while(x>1){
                    numChocolate++;
                    chocolate[numChocolate]=i;
                    x--;
                }
            }
            else if(x==0){
                numBox++;
                box[numBox]=i;
            }
        }

        for(int i=1;i<=numChocolate;i++){
            for(int j=1;j<=numBox;j++){
                int x=abs(chocolate[i]-box[j]);
                int y=n-abs(chocolate[i]-box[j]);
                G[i][j]=-min(x,y);
            }
        }

        n=numChocolate;
        m=numBox;

        printf("%d\n",-KM());
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值