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;
}