CF-1650F Vitaly and Advanced Useless Algorithms

重点:如何保存背包DP,最优解的组成。

题目大意:

有n个作业,m个动作。每一个动作都只能对1个任务进行操作。给出每个动作作用的任务编号、作用时间、完成度、当一个任务的完成度达到100,则表示该任务可以被完成。

但是每个任务也都有截止时间,如果该动作无法在截止时间前完成,则失败。

输出:

如果任务无法按时完成,输出-1.

否则,第一行,输出可以完成任务的动作数目;第二行,输出动作序列。

:只要可以完成任务,可以输出1个任意的解决方案。也就是动作数和动作先后顺序并不做要求)

思路:
本题目可以视作01背包。

当我们把时间视为:物品的价值;完成度视为:物品的重量;背包容量:100。

接下来就是套用01背包DP就可以解决。

但是呢~~~~题目还让你输出:可以完成所有任务的动作序列。这就是——我在这题新学到的......

我们根绝可以被放入的物品的顺序,从后往前遍历。如果当前的dp[i][j]=dp[i-1][j],说明当前点的dp[i][j]的最小值,继承于i-1。如图中的x2一定是继承于x1的值。

所以,当我们遇到dp[i][j]!=dp[i-1][j],当前i,一定是最优解的构成之一。

代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define endl '\n'
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define mm(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;

struct point{
	ll tt,res,id; 
};
ll a[100010];
vector<point>vec[100010]
ll now[100010];
ll vis[110];
vector<ll>ans;

ll cc(ll x){
	ll dp[vec[x].size()+10][110];
	memset(dp,INF,sizeof(dp));
	dp[0][0]=0;
	for(ll i=0;i<vec[x].size();i++){
		dp[i+1][0]=0;
		ll tt=vec[x][i].tt,res=vec[x][i].res,id=vec[x][i].id;
		for(ll j=1;j<=100;j++){
			dp[i+1][j]=min(dp[i][j],dp[i][max(1ll*0,j-res)]+tt);
		}
	}
	if(dp[vec[x].size()][100]==-1)return -1;
	for(ll i=100,k=vec[x].size()-1;k>=0;k--){
		if(dp[k+1][i]==dp[k][i])continue;
		ans.push_back(vec[x][k].id);
		i=max(1ll*0,i-vec[x][k].res); 
	}
	return dp[vec[x].size()][100];
}

void solve(){
	ans.clear();
	ll n,m;
	ll num,t,res;
	cin>>n>>m;
	for(ll i=1;i<=n;i++)vec[i].clear();
	for(ll i=1;i<=n;i++){
		cin>>a[i];
	}
	for(ll i=1;i<=m;i++){
		cin>>num>>t>>res;
		vec[num].push_back({t,res,i});
	}
	ll sum=0;
	for(ll i=1;i<=n;i++){
		now[i]=cc(i);
		if(now[i]==-1){
			cout<<"-1"<<endl;
			return ;
		}
		sum+=now[i];
		if(sum>a[i]){
			cout<<"-1"<<endl;
			return ;
		}
	}
	cout<<ans.size()<<endl;
	for(ll i=0;i<ans.size();i++)cout<<ans[i]<<" ";
	cout<<endl;
}

int main(){
	IOS;
	int _=1;
	cin>>_;
	while(_--){
		solve();
	}
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值