poj1275差分约束

设num[i]  为在i时刻可以工作的应聘人数

x[i]  在i时刻招聘的人数

m[i] 在i时刻要求最少的工作人数

由题意建立起约束:

(1)0 <= x[i] <= num[i]

(2)x[i-7] + x[i-6 ]+.....+ x[i] >= m[i] (因为工作8小时) (i<7时要特殊考虑)

设s[i] = x[1] + x[2] + .....+ x[i]  从第0时刻到第i时刻招聘的总人数

将约束转化为 差分形式:

由(1):

s[i] - s[i-1] >= 0  1 <= i <= 24  ( 第i时刻可能不招人)

s[i-1] - s[i] >= -num[i]    1 <= i <= 24 

由(2):

s[i] - s[i-8] >= m[i]  9 <= i <= 24 (不受一天的影响)

s[i] - s[i+16] >= m[i] - s[24] 1 <= i <= 8 (受上一天的影响)

特加条件: s[24] - s[0] >= ans 

不妨假设s[24]为ans,以0点为源点跑最长路有解,且s[24]=ans。说明此解有效。

所以最长路作为二分搜索的判断条件。搜索可行解。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <queue>
#define MAX_N 25
#define INF 0x7fffffff
using namespace std;
struct edge{
	int to,val;
};
bool inque[MAX_N];
int dist[MAX_N];
int vis[MAX_N];
int s[MAX_N];
int num[MAX_N];
int m[MAX_N];
vector<edge> g[MAX_N];
int init()
{
	for(int i=0;i<MAX_N;++i)
		g[i].clear();
}
void add_edge(int u,int v,int val)
{
	g[u].push_back((edge){v,val});
}
void bulidGraph(int ans)
{
	// s[i] – s[i-1] >= 0
	// s[i-1] – s[i] >= –num[i]
 	// s[i] – s[i-8] >= r[i], 9 <= i <= 24
 	// s[i] – s[i+16] >= r[i] – s[24],  1<= i <= 8
	 // s[24] – s[0] >= ans
	for(int i=1;i<=24;++i)
		add_edge(i-1,i,0);
	for(int i=1;i<=24;++i)
		add_edge(i,i-1,-num[i]);
	for(int i=9;i<=24;++i)
		add_edge(i-8,i,m[i]);
	for(int i=1;i<=8;++i)
		add_edge(i+16,i,m[i]-ans);
	add_edge(0,24,ans);
}
bool spfa(int start,int res)
{
	queue<int> que;
	for(int i=0;i<MAX_N;++i) 
		dist[i]=-INF;
	memset(inque,0,sizeof(inque));
	memset(vis,0,sizeof(vis));
	dist[start]=0;
	que.push(start);
	inque[start]=1;
	while(!que.empty())
	{
		int u=que.front();que.pop();
		inque[u]=0;
		++vis[u];
		if(vis[u]>MAX_N) return false;
		for(int i=0;i<g[u].size();++i)
		{
			edge &e=g[u][i];
			if(dist[e.to]<dist[u]+e.val)
			{
				dist[e.to]=dist[u]+e.val;
				if(!inque[e.to])
				{
					que.push(e.to);
					inque[e.to]=1;
				}
			}
		}
	}
	if(dist[24]==res)
	{
		return true;
	}
	return false;
}
int solve(int rb)
{
	int lb=0;
	rb*=2;
	while(rb-lb>=1){
		int mid=(rb+lb)/2;
		init();
		bulidGraph(mid);
		if(spfa(0,mid))
			rb=mid;
		else lb=mid+1;
	}
	return rb;
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		for(int i=1;i<=24;++i)
			scanf("%d",&m[i]);
		int n;
		memset(num,0,sizeof(n));
		scanf("%d",&n);
		int u,rb=0;
		for(int i=0;i<n;++i)
		{
			scanf("%d",&u);
			++rb;++num[u+1];
		}
		int ans=solve(rb);
		if(ans<=rb)
			printf("%d\n",ans);
		else printf("No Solution\n");
	}
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值