差分约束——HDOJ 1529

HDOJ 1529 Cashier Employment

断断续续做了3天才弄明白,真是一道经典题

/*
HDOJ 1529 Cashier Employment
s[i]保存从24时刻开始到i时刻的雇佣人数
t[i]保存i时刻能够上班的人数
r[i]保存i时刻至少需要的人数

只要开始工作就要连续工作8小时,有下面的约束条件

0 <= s[i]-s[i-1] <= t[i]
s[i]-s[i-8] >= r[i] (8 <= i <= 23)
s[23] - (s[i+16] - s[i]) >= r[i] (0 <= i <= 7)
s[23] - s[24] >= result

整理为下面的工整的约束条件
s[i-1] - s[i] <= 0  这个条件在第三和第四个条件的时候被更新了,所以不用加这个也没事
s[i] - s[i-1] <= t[i]
s[i-8] - s[i] <= - r[i]   (8 <= i <= 23)
s[i+16] - s[i] <= -r[i]+result   (0 <= i <= 7)
s[24] -s[23] <= -result

result 是未知的,但是范围就在1到申请人总数之间,用二分枚举
要求的是满足要求的最小值
*/

#include <iostream>
using namespace std;

#define maxn 25
#define mx 1000

int r[maxn];
int t[maxn];
int d[maxn];

struct E
{
    int u,v,w;
};

E edge[mx];
int edno;

bool Bellman_Ford(int sum)
{
    int i,j,t;
    bool change;
    int m=edno;

	//s[i+16] - s[i] <= -r[i]+result
    for(i=0;i<=7;i++)
	{
        edge[m].u=i;
        edge[m].v=i+16;
        edge[m++].w=-(r[i]-sum);
    }
	
	//s[24] -s[23] <= -result
    edge[m].u=23;
    edge[m].v=24;
    edge[m++].w=-sum;
    
	for(i=0;i<=24;i++)
		d[i]=0;

    for(i=0;i<=24;i++)
	{
        change=false;
        for(j=0;j<m;j++)
		{
            t=d[edge[j].u]+edge[j].w;
            if(d[edge[j].v]>t)
			{
                d[edge[j].v]=t;
                change=true;
            }
        }
    }
    if(change)
		return false;
    return true;
}

int main()
{
    int ncase,i,n,tmp,left,right,mid,res;
    cin>>ncase;
    while(ncase--)
	{
        for(i=0;i<24;i++)
			cin>>r[i];
        edno=0;
        memset(t,0,sizeof(t));
		cin>>n;
        for(i=0;i<n;i++)
		{
            cin>>tmp;
            t[tmp]++;
        }
		
		//s[i] - s[i-1] <= t[i]
        edge[edno].u=24;
        edge[edno].v=0;
        edge[edno++].w=t[0];
        for(i=1;i<=23;i++)
		{
            edge[edno].u=i-1;
            edge[edno].v=i;
            edge[edno++].w=t[i];
        }
		//s[i-8] - s[i] <= - r[i]
        for(i=8;i<=23;i++)
		{
            edge[edno].u=i;
            edge[edno].v=i-8;
            edge[edno++].w=-r[i];
        }
        left=0;right=n;
        res=-1;
        while(left<right)
		{
            mid=(left+right)/2;
            if(Bellman_Ford(mid))
			{
                res=mid;
                right=mid;
            }
            else 
			{
                left=mid+1;
            }
        }
        if(res==-1) 
			cout<<"No Solution"<<endl;
        else 
			cout<<res<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值