poj1275 差分约束难再建模

题意;

Tehran的一家每天24小时营业的超市,需要一批出纳员来满足它的需要。超市经理雇佣你来帮他解决他的问题——超市在每天的不同时段需要不同数目的出纳员(例如:午夜时只需一小批,而下午则需要很多)来为顾客提供优质服务。他希望雇佣最少数目的出纳员。

经理已经提供你一天的每一小时需要出纳员的最少数量——R(0), R(1), ..., R(23)。R(0)表示从午夜到上午1:00需要出纳员的最少数目,R(1)表示上午1:00到2:00之间需要的,等等。每一天,这些数据都是相同的。有N人申请这项工作,每个申请者I在每24小时中,从一个特定的时刻开始连续工作恰好8小时,定义tI (0 <= tI <= 23)为上面提到的开始时刻。也就是说,如果第I个申请者被录取,他(她)将从tI 时刻开始连续工作8小时。

你将编写一个程序,输入R(I)(I = 0..23)和tI (I = 1..N),它们都是非负整数,计算为满足上述限制需要雇佣的最少出纳员数目。在每一时刻可以有比对应的R(I)更多的出纳员在工作。

输入
    输入文件的第一行为测试点个数(<= 20)。每组测试数据的第一行为24个整数表示R(0),R(1),..., R(23)(R(I)<= 1000)。接下来一行是N,表示申请者数目(0 <= N <= 1000),接下来每行包含一个整数tI (0 <= tI <= 23)。两组测试数据之间没有空行。

输出
    对于每个测试点,输出只有一行,包含一个整数,表示需要出纳员的最少数目。如果无解,你应当输出“No Solution”。

样例输入

1
1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
5
0
23
22
1
10

样例输出

1

算法分析:

设num[i] 为来应聘的在第i个小时开始工作的人数

           r[i] 为第i个小时至少需要的人数

           x[i] 为招到的在第i个小时开始工作的人数

       根据题意有:

         0 <= x[i] <= num[i]

         x[i] + x[i-1] + …+ x[i-7] >= r[i] (题目中的连续工作8小时)

      再设 s[i] = x[1] + … + x[i]

      则有: s[i] – s[i-1] >= 0

                    s[i-1] – s[i] >= –num[i]

                    s[i] – s[i-8] >= r[i], 8 <= i <= 24

                    s[i] – s[i+16] >= r[i] – s[24],  1<= i <= 7

      还需要添加一个隐藏不等式: s[24] – s[0] >= ans(枚举的答案)

      通过枚举s[24],来检测是否满足条件,题目是求最小值,即求最长路,以0为源点。

代码:

 

 

#include <stdio.h>

#define maxN 25//最大顶点数,这里是s[i]
#define inf 0x7fffffff

struct Edge 
{
	int v, w, next;
}edge[maxN * 30];//边

int edgeNum;//边总数
int dis[maxN];//最长路径中的dis[i]代表远点到i的最长距离
int r[maxN];//r[i]标示i时刻至少需要多少人
int num[maxN];//num[i]标示i时刻开始工作的人
bool vis[maxN];//标示i是否进入队列
int preEdge[maxN];//同一个起点的上一条边
int cnt[maxN];//入队列的次数
int queue[maxN * 30];//模拟队列
int n;//输入的n

void addEdge(int u, int v, int w)//添加新边
{
	edge[edgeNum].v = v;
	edge[edgeNum].w = w;
	edge[edgeNum].next = preEdge[u];//以u为起点的上一条边
	preEdge[u] = edgeNum ++;
}

int spfa(int ans)//spfa算法实现
{
	int head = 0, tail = 1;
	
	for (int i = 0; i <= 24; ++ i)
	{
		dis[i] = -inf;
		vis[i] = false;
		cnt[i] = 0;
	}
	queue[0] = 0;
	dis[0] = 0;
	while (head < tail)
	{
		int u = queue[head];
		int p = preEdge[u];
		vis[u] = true;
		while (p != -1)
		{
			int v = edge[p].v, w = edge[p].w;
			if (dis[v] < dis[u] + w)
			{
				dis[v] = dis[u] + w;
				if (!vis[v])
				{
					vis[v] = true;
					queue[tail] = v;
					tail ++;
					
				}
				if(++cnt[v] > 24)
				{
					return 0;
				}
				
			}
			p = edge[p].next;
		}
		head ++;
		vis[u] = false;
	}
	if (dis[24] == ans)
	{
		return 1;
	}
	return 0;
}

void buildGraph(int ans)//建图这也是本题目的关键,建模。。。。
{
	edgeNum = 0;
	for (int i = 0; i <= 24; ++ i)
	{
		preEdge[i] = -1;
	}
	addEdge(0,24,ans);  
	for(int i = 1;i <= 24;++i)  
	{   
		addEdge(i - 1,i,0);  
		addEdge(i,i - 1,-num[i]);  
	}  
	for(int i = 1;i <= 8;++i)    
		addEdge(i + 16,i,r[i] - ans);  
	for(int i = 9;i <= 24;++i)   
		addEdge(i - 8,i,r[i]);  

}

int main()//主函数
{
	int	cases;
	scanf("%d", &cases);
	while (cases --)
	{
		bool flag = false;;
		for (int i = 1; i <= 24; ++ i)
		{
			scanf("%d", &r[i]);
			num[i] = 0;
		}
		scanf("%d", &n);
		int t;
		for (int i = 0; i < n; ++ i)
		{
			scanf("%d", &t);
			num[t + 1] ++; 
		}
		for (int i = 0; i <= n; ++ i)
		{
			buildGraph(i);
			if (spfa(i) == 1)
			{
				flag = true;
				printf("%d\n", i);
				break;
			}
		}
		if (!flag)
		{
			printf("No Solution\n");  
		}
	}
	return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值