非单位时间任务安排问题 贪心 动态规划 回溯

问题描述:

在这里插入图片描述

动态规划+贪心思想:

(这题好像一般归类在贪心,但我感觉主要就是个动态规划)
1.首先将任务按其截止时间非减序排序。
2.对任务 1 , 2 , …… , i,如果截止时间为 d ,则最小误时惩罚为 p( i , d ) 。
3.对第一个任务来说:完成任务时间t<=任务截止时间,这个任务可以做完,没有惩罚时间;否则不做,有惩罚时间。
在这里插入图片描述
4.对于其他任务:
①如果任务时间t>任务截止时间,不能执行该任务。
p( i , d ) =p(i-1, d)+wi
②如果任务时间t<=任务截止时间,可以执行也可以不执行。
p( i , d ) = min{ p(i-1, d)+wi , p(i-1, min{d, di}-ti) }
前者表示不执行任务,后者表示执行(这时必须在第 i 个任务的截至时间前做完它(即 min{d, di}-ti ))

代码:

#include <iostream>
#include <algorithm> 
using namespace std;
struct task{
	int t;//任务时间
	int d;//截止时间
	int w;//惩罚时间 
}; 
bool cmp(task x,task y)
{
	return x.d<y.d;
}
int main()
{
	int n;cin>>n;//输入任务个数
	task a[n+1];
	for(int i=1;i<=n;++i)
		cin>>a[i].t>>a[i].d>>a[i].w;
	sort(a+1,a+n+1,cmp); //按照截止时间非减排序 
	int p[n+1][n+1];
	/*处理第一个任务*/
	for(int i=0;i<=a[n].d;i++)
    {
        if(i<a[1].t) p[1][i]=a[1].w;//罚
        else p[1][i] = 0;//做第一个任务 
    }
    /*处理第2~n个任务*/
    for(int i=2;i<=n;++i)
		for(int j=0;j<=a[n].d;++j)
		{
			int x=p[i-1][j]+a[i].w;
			int y=p[i-1][min(j, a[i].d)-a[i].t];
			if (j<a[i].t) p[i][j]=x;//当前任务无法做 
			else p[i][j]=min(x,y);//可做可不做,选最优 
   		 }
	cout<<p[n][a[n].d]<<endl;
	return 0;
}

回溯法思路:

1.首先将任务按其截止时间非减序排序。
2.解空间可以用子集树表示。一共n个任务,节点左子树表示这个任务做,右子树不做。
3.变量t:当前所有做了的任务时间之和。
变量w:当前惩罚时间和。
变量bestw:记录最短的惩罚时间。
4.约束条件:t<=di(总时间t<=当前任务截止时间),否则剪枝。
限界函数:w<=bestw(当前惩罚时间一定要小于最优惩罚时间),否则没有继续dfs的必要,直接剪枝。

回溯法代码:

#include <iostream>
using namespace std;
int n;//任务个数 
struct task{
	int t;//任务时间
	int d;//截止时间
	int w;//惩罚时间 
}; 
task a[100];
int bestw;//存储最短的任务惩罚时间
int w;//当前的任务惩罚时间
int t;//当前所做任务总时间 
int x[100]; 

void backtrack(int i)
{
	if(i>n)//到达叶节点 
	{bestw=w;return;} 
	/*约束条件:所做任务总时间是否<=当前任务截止时间,成立进入左子树*/
	if(t+a[i].t<=a[i].d) 
	{	
		t+=a[i].t;//更新总时间 
		backtrack(i+1);
		t-=a[i].t;//回溯结束时时间要还原 
	} 
	/*限界函数:当前总惩罚是否<=bestw,成立进入右子树,否则剪枝*/ 
	if(w+a[i].w<=bestw) 
	{
		w=w+a[i].w;
		backtrack(i+1);
		w=w-a[i].w;
	}
}
int main()
{
	cin>>n;//输入任务个数
	w=0;t=0;bestw=1000;//先将bestw设置为一个较大的值 
	for(int i=1;i<=n;++i)
		cin>>a[i].t>>a[i].d>>a[i].w;
	backtrack(1);
	cout<<bestw<<endl;
	return 0; 
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值