poj 1040

又是一个典型的dfs加剪枝的搜索的题目:

题目大意:从A站到B站,共n个车站,每个车站可以有不同的上车人数的请求,见题目的描述。

问题:在所有的ticket order过程中,可以获得的最大利润。

1、我用回溯法,进行暴力求解,写得比较简单,没有什么技术含量,且效率是比较低下的,1000MS一次水过,下面会附上代码:

2、后来采用了dfs的方法,加了一个剪枝,16MS,这个算是比较过得去。

分析:

struct Data

{

int x; // start station

int y; // end station

int p; // number of passengers

int v; // profit of the order

int r; // the maximum profit by accept the order 

};

解释一下int r在这里的作用,这里用于搜索中的剪枝

1)先按利润的大小值进行排序

2)初始化每个节点的r值,第i个节点的r值在这里表示的意思是:若第i个节点后,理论上可以达到的最大利润值的大小。这个条件可以用来剪枝,而且效果非常明显。

下面dfs方式实现的代码:

/* poj 1040 , wrote by Dream Chen 2010/12/11 */

#include <cstdio>

#include <string>

#include <algorithm>

using namespace std;

 

struct Data

{

int x; // start station

int y; // end station

int p; // number of passengers

int v; // profit of the order

int r; // the maximum profit by accept the order 

};

Data data[50];

int capacity = 0;

int station = 0;

int order = 0;

int stop[50];

int maxp = 0;

 

void Initial(void);

inline bool cmp(const Data &a, const Data &b)

{

return a.v > b.v;

}

void dfs(int s,int curMaxp);

int main(void)

{

//freopen("input.txt","r",stdin);

while(scanf("%d%d%d",&capacity,&station,&order) && !(capacity == 0 && station == 0 && order == 0))

{

Initial();

for (int i = 0; i < order; ++i)

{

scanf("%d%d%d",&data[i].x, &data[i].y, &data[i].p);

data[i].v += (data[i].y - data[i].x) * data[i].p;

}

sort(data,data+order,cmp);

for (int i = order - 1, sum = 0; i >= 0; --i)

{

sum += data[i].v;

data[i].r = sum;

}

maxp = 0;

dfs(0,0);

printf("%d/n",maxp);

}

return 0;

}

void Initial(void)

{

memset((void*)data,0,sizeof(data));

memset((void*)stop,0,sizeof(stop));

maxp = 0;

}

void dfs(int s,int curMaxp)

{

if (curMaxp > maxp)

{

maxp = curMaxp;

}

if (s >= order)

{

return;

}

/* If accept this order, curMaxp + data[s].r < maxp , 

  it means that this solution can't be the optimal solution

  It's very important here!!!

*/

if (curMaxp + data[s].r < maxp)

{

return;

}

for (int i = s, j = 0; i < order; ++i)

{

for (j = data[i].x; j < data[i].y; ++j)

{

stop[j] += data[i].p;

if (stop[j] > capacity)

{

break;

}

}

if (j == data[i].y)

{

dfs(i+1,curMaxp+data[i].v);

--j;

}

// traceback here

for (int k = j; k >= data[i].x; --k)

{

stop[k] -= data[i].p;

}

}

}

 

下面是用回溯法暴搜的代码:

/* poj 1040, wrote by Dream Chen 2010/11/30*/
#include <cstdio>
#include <string>
using namespace std;

struct Data
{
	int start;
	int dst;
	int num_psng;
};

int icapacity = 0;
int ibstation = 0;
int iticket = 0;
Data data[30];
int res = 0;
int cur_num = 0;
bool bflag[30];
int arr_psng_num[30];

void Initial(void);
void TrySearch(int cur);
bool Test(int i);
void AddOrder(int cur);
void RemoveOrder(int cur);
int main(void)
{
	while(scanf("%d%d%d", &icapacity, &ibstation, &iticket) != 0)
	{
		if (0 == icapacity && 0 == ibstation && 0 == iticket)
		{
			break;
		}
		Initial();
		for (int i = 0; i < iticket; ++i)
		{
			scanf("%d%d%d",&data[i].start,&data[i].dst,&data[i].num_psng);
		}
		TrySearch(0);
		printf("%d/n",res);
	}
	return 0;
}

void Initial(void)
{
	memset((void*)data,0,sizeof(data));
	memset((void*)arr_psng_num,0,sizeof(arr_psng_num));
	res = 0;
	cur_num = 0;
}

void TrySearch(int cur)
{
	if (cur >= iticket)
	{
		return;
	}
	if (Test(cur))
	{
		AddOrder(cur);
		TrySearch(cur+1);
		RemoveOrder(cur);
	}
	TrySearch(cur+1);
}

bool Test(int i)
{
	for (int j = data[i].start; j < data[i].dst; ++j)
	{
		if (arr_psng_num[j] + data[i].num_psng > icapacity)
		{
			return false;
		}
	}
	return true;
}

void AddOrder(int cur)
{
	bflag[cur] = true;
	if (cur >= 0 && cur < iticket)
	{
		for (int j = data[cur].start; j < data[cur].dst; ++j)
		{
			arr_psng_num[j] += data[cur].num_psng;
			cur_num += data[cur].num_psng;
		}
	}
	if (cur_num > res)
	{
		res = cur_num;
	}
}

void RemoveOrder(int cur)
{
	bflag[cur] = false;
	if (cur >= 0 && cur < iticket)
	{
		for (int j = data[cur].start; j < data[cur].dst; ++j)
		{
			arr_psng_num[j] -= data[cur].num_psng;
			cur_num -= data[cur].num_psng;
		}
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值