尽梨了(贪心+dp)
盡梨了
- problem
- solution
- code
problem
豐之崎學園附近共有 nnn 個商店,在時刻 000 時,英梨梨從學園出發開始購物。
從學園走到任意一個商店,或從一個商店走到另一個商店需要 111 單位時間。
如果英梨梨在時刻 ttt 到達了商店 iii ,她需要先花費 ai×t+bia_i × t + b_iai?×t+bi? 的時間排隊,
然后才能購買這家商店的物品。
英梨梨想知道,在時刻 T+0.5T + 0.5T+0.5 之前她最多能在多少個不同商店買到物品呢?
這里假定只會在走路和排隊上消耗時間,購買物品不消耗時間。
n≤2e5,T≤1e9n\le 2e5,T\le 1e9n≤2e5,T≤1e9。
solution
假設在 ttt 時刻選擇 iii 商店購買,然后立即到 jjj 商店購買,則 jjj 商店會多付出 (ai?t+bi+1)?aj(a_i·t+b_i+1)·a_j(ai??t+bi?+1)?aj? 的時間。
假設在 ttt 時刻選擇 jjj 商店購買,然后立即到 iii 商店購買,則 iii 商店會多付出 (aj?t+bj+1)?ai(a_j·t+b_j+1)·a_i(aj??t+bj?+1)?ai? 的時間。
若選擇先 iii 后 jjj 的順序,則需滿足 (ai?t+bi+1)?aj<(aj?t+bj+1)?ai?(bi+1)?aj<(bj+1)?ai(a_i·t+b_i+1)·a_j<(a_j·t+b_j+1)·a_i\Rightarrow (b_i+1)·a_j<(b_j+1)·a_i(ai??t+bi?+1)?aj?<(aj??t+bj?+1)?ai??(bi?+1)?aj?<(bj?+1)?ai?。
發現商店的先后順序與時間 ttt 無關。
所以可以按照這個關系進行商店排序。
最后實際去的商店順序按照時間排序肯定是按這個關系排序后形成的新序列的一個子序列。
暴力地設 dpi,j:dp_{i,j}:dpi,j?: 在遍歷到 iii 商店(已經排序過),在 jjj 個商店買到物品的最少花費時間,轉移考慮是否進入 iii 商店即可,時間復雜度 O(n2)O(n^2)O(n2)。
考慮特殊數據點 ?iai>0\forall_i\ a_i>0?i??ai?>0。
通過時間轉移公式可以發現,如果 ai≠0a_i\neq 0ai??=0,那么進入商店后的時間是指數級增長的。
這就意味著轉移的第二位只需要開到 log?T\log TlogT 級別就可以了。
實際上,這已經幾乎是正解了。
將所有 ai=0a_i=0ai?=0 的商店排到最后,只考慮 ai≠0a_i\neq 0ai??=0 的商店,按照上面優化后的 dpdpdp 做即可。
最后考慮剩余的時間 T?dpk,iT-dp_{k,i}T?dpk,i? 后面能接多少個 ai=0a_i=0ai?=0 的商店,貪心的將這些特殊商店按照 bbb 升序排序。
時間復雜度:O(nlog?n+nlog?T)O(n\log n+n\log T)O(nlogn+nlogT)。
code
#include <cmath> #include <cstdio> #include <iostream> #include <algorithm> using namespace std; #define int long long #define maxn 200005 struct node { int a, b; }it[maxn]; int n, m, T, k; int dp[maxn][35];void read( int &x ) {x = 0; char s = getchar();while( s < '0' or s > '9' ) s = getchar();while( '0' <= s and s <= '9' ) x = ( x << 1 ) + ( x << 3 ) + ( s ^ 48 ), s = getchar(); }signed main() {freopen( "eriri.in", "r", stdin );freopen( "eriri.out", "w", stdout );read( n ), read( T );m = log( T ) / log( 2 );for( int i = 1;i <= n;i ++ ) read( it[i].a ), read( it[i].b );sort( it + 1, it + n + 1, []( node x, node y ) { return ( x.b + 1 ) * y.a < ( y.b + 1 ) * x.a; } );while( it[k + 1].a ) k ++;for( int i = 0;i <= n;i ++ )for( int j = 1;j <= m;j ++ ) dp[i][j] = T + 1;for( int i = 1;i <= k;i ++ )for( int j = 1;j <= m;j ++ )dp[i][j] = min( min( T + 1, dp[i - 1][j] ), dp[i - 1][j - 1] + 1 + ( dp[i - 1][j - 1] + 1 ) * it[i].a + it[i].b );sort( it + k + 1, it + n + 1, []( node x, node y ) { return x.b < y.b; } );int ans = 0;for( int i = 0;i <= m and dp[k][i] <= T;i ++ ) {int sum = T - dp[k][i], cnt = 0;for( int j = k + 1;j <= n and sum > it[j].b;j ++ )cnt ++, sum -= it[j].b + 1;ans = max( ans, i + cnt );}printf( "%lld\n", ans );return 0; }總結
以上是生活随笔為你收集整理的尽梨了(贪心+dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 华为智选车业务首款轿车智界S7发布 预售
- 下一篇: 团不过(dp)