背包方案-cf1650F

F. Vitaly and Advanced Useless Algorithms

题目大意:

n个任务和m种操作,每个任务有他的ddl,i操作可以花费ti时间使得ei任务完成百分之pi。问能否在所有任务的ddl前完成所有任务,若能则输出要执行哪些操作,若不能输出-1 。

思路和代码:

对于每一种任务分开考虑,做一个背包。在选择背包的时候,可以选择时间作为容量,完成度作为价值;或者完成度作为容量,时间作为价值。

因为我们需要在最短的时间李做完这件事,所以我们选择完成度为容量,时间作为价值,min的一个01背包。

然后就是01背包的方案问题。

有两种解法,法二不知道为啥RTE 了我太菜了估计没码好

法一:用一维滚动数组,在每个点下面挂一条操作序列,操作序列随着dp数组转移而转移。

法二:二维dp数组,做完后逆推,有:(dp[i][pst-a[i].p]+a[i].t==tm)的时候可以向前回溯。

void solve2(){
	ll n , m ;
	cin >> n >> m ;
	vct<ll> ddl(n + 1 , 0) ;
	rep(i , 1 , n) cin >> ddl[i] ;
	
	vct<vct<op> > tsk(n + 1 , vct<op>(1 , {0 , 0 , 0})) ;
	rep(i , 1 , m){
		ll e , t , p ;
		cin >> e >> t >> p ;
		tsk[e].pb({i , t , p}) ;
	}
	ll cur = 0 ;
	vct<ll> ans ;
	rep(e , 1 , n){
		vct<ll> dp(120 , INF) ;
		vct<vct<ll> > pre(120) ;//操作序列
		dp[0] = 0 ;
		ll num = tsk[e].size() - 1 ;
		rep(i , 1 , num){
			ll pp = tsk[e][i].p ;
			ll tt = tsk[e][i].t ;
			drep(j , 0 , 100){//枚举pst,j代表转移前的点 
				ll nj = min(1LL * 100 , j + pp) ;//nj代表转移到的点 
				if(dp[nj] > dp[j] + tt ){
					dp[nj] = dp[j] + tt ;
					pre[nj] = pre[j] ;//操作序列转移
					pre[nj].pb(tsk[e][i].idx) ;
				}
			}
		}
		cur += dp[100] ;//若到不了dp[100]为INF,也是会退出的
		if(cur > ddl[e]){
			cout << "-1\n" ;
			return ;
		}
		for(auto x : pre[100]) ans.pb(x) ;
	}
	cout << ans.size() << "\n" ;
	for(ll x : ans) cout << x << " " ; cout << "\n" ;
	
}
小结:

比较标准的01背包方案问题。

posted @ 2022-03-10 15:57  tyrii  阅读(75)  评论(0)    收藏  举报