题目传送门
题解:
需要注意到的是 每个offer都获益都是会随着时间的增加而渐少(或不变)。
所以我们可以知道,最多在第n个月的时候这个人会买车离开。
solve1:最优2分图匹配
我们可以把每个月都和每个offer建边。
val[i][j]代表的是离开前倒数第i个月获取了第j个月的offer, 所以边权就是 max(0ll, a-min(j-1,k)*b).
最后跑一遍km。
所以复杂度是 n^3.
代码:
1 /* 2 code by: zstu wxk 3 time: 2019/02/02 4 */ 5 #include6 using namespace std; 7 #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); 8 #define LL long long 9 #define ULL unsigned LL 10 #define fi first 11 #define se second 12 #define pb push_back 13 #define lson l,m,rt<<1 14 #define rson m+1,r,rt<<1|1 15 #define lch(x) tr[x].son[0] 16 #define rch(x) tr[x].son[1] 17 #define max3(a,b,c) max(a,max(b,c)) 18 #define min3(a,b,c) min(a,min(b,c)) 19 typedef pair<int,int> pll; 20 const int inf = 0x3f3f3f3f; 21 const int _inf = 0xc0c0c0c0; 22 const LL INF = 0x3f3f3f3f3f3f3f3f; 23 const LL _INF = 0xc0c0c0c0c0c0c0c0; 24 const LL mod = (int)1e9+7; 25 const int N = 510; 26 int n; 27 LL val[N][N]; 28 LL lx[N], ly[N], slack[N]; 29 int linky[N]; 30 LL pre[N]; 31 bool vis[N], visx[N], visy[N]; 32 void bfs(int k){ 33 LL px, py = 0,yy = 0, d; 34 memset(pre, 0, sizeof(LL) * (n+2)); 35 memset(slack, inf, sizeof(LL) * (n+2)); 36 linky[py]=k; 37 do{ 38 px = linky[py],d = INF, vis[py] = 1; 39 for(int i = 1; i <= n; i++) 40 if(!vis[i]){ 41 if(slack[i] > lx[px] + ly[i] - val[px][i]) 42 slack[i] = lx[px] + ly[i] -val[px][i], pre[i]=py; 43 if(slack[i] i; 44 } 45 for(int i = 0; i <= n; i++) 46 if(vis[i]) lx[linky[i]] -= d, ly[i] += d; 47 else slack[i] -= d; 48 py = yy; 49 }while(linky[py]); 50 while(py) linky[py] = linky[pre[py]] , py=pre[py]; 51 } 52 void KM(){ 53 memset(lx, 0, sizeof lx); 54 memset(ly, 0, sizeof ly); 55 memset(linky, 0, sizeof(int)*(n+2)); 56 for(int i = 1; i <= n; i++) 57 memset(vis, 0, sizeof(bool)*(n+2)), bfs(i); 58 } 59 void input(){ 60 scanf("%d", &n); 61 LL a, b, k; 62 for(int i = 1; i <= n; ++i){ 63 scanf("%I64d%I64d%I64d", &a, &b, &k); 64 for(LL j = 0; j < n; ++j){ 65 val[i][j+1] = max(0ll, a-min(j,k)*b); 66 } 67 } 68 } 69 int main(){ 70 input(); 71 KM(); 72 LL ans = 0; 73 for(int i = 1; i <= n; ++i) 74 ans += lx[i] + ly[i]; 75 printf("%lld\n", ans); 76 return 0; 77 }
solve2:DP
首先需要明白一点,就是如果所有offer 还款的期限还没有到的话,那么肯定是bi越大的offer越后拿更优。
所以把所有的offer按bi大小sort一下,大的排在前面。
dp[i][j] 代表的是 处理到第i个物品, 倒数第j个月的花费是多少。
所以,最显然的转移方式就是 dp[i][j] = max(dp[i][j], dp[i-1][j-1] + max(0ll, A[i].a - (j-1)*A[i].b)).
还需要明白的一点就是,如果bi越大,理论上是放在越后拿越好,但是到如果完全还完贷款的那种offer,是越早拿越好,可以让别的offer少还一个月的贷款。
所以又存在另一种转移方式 dp[i][j] = dp[i-1][j] + max(0ll, A[i].a - A[i].k*A[i].b);
代码:
/* code by: zstu wxk time: 2019/02/02 */ #includeusing namespace std; #define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout); #define LL long long #define ULL unsigned LL #define fi first #define se second #define pb push_back #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lch(x) tr[x].son[0] #define rch(x) tr[x].son[1] #define max3(a,b,c) max(a,max(b,c)) #define min3(a,b,c) min(a,min(b,c)) typedef pair<int,int> pll; const int inf = 0x3f3f3f3f; const int _inf = 0xc0c0c0c0; const LL INF = 0x3f3f3f3f3f3f3f3f; const LL _INF = 0xc0c0c0c0c0c0c0c0; const LL mod = (int)1e9+7; const int N = 550; struct Node{ LL a, b, k; bool operator<(const Node & x) const{ return b > x.b; } }A[N]; int n; LL dp[N][N]; void Ac(){ for(int i = 1; i <= n; ++i) scanf("%I64d%I64d%I64d", &A[i].a, &A[i].b, &A[i].k); sort(A+1, A+1+n); for(int i = 1; i <= n; ++i){ for(int j = 0; j <= n; ++j){ dp[i][j] = dp[i-1][j] + max(0ll, A[i].a - A[i].k*A[i].b); if(j) dp[i][j] = max(dp[i][j], dp[i-1][j-1] + max(0ll, A[i].a - (j-1)*A[i].b)); } } LL ans = 0; for(int i = 0; i <= n; ++i) ans = max(ans, dp[n][i]); printf("%I64d\n", ans); } int main(){ while(~scanf("%d", &n)){ Ac(); } return 0; }