题意:给出$N$个直角三角形拼图和$M imes M$的网格,第$i$个直角三角形水平直角边边长为$frac{1}{a_i}$,垂直直角边边长为$frac{1}{b_i},$规定直角三角形只能直角顶点在右上方地摆放,直角顶点必须摆放在网格的顶点处,且水平直角边和垂直直角边必须与网格的某一条线重合,三角形可以越出网格。现在你可以将每个三角形都放大正整数$K$倍,求存在一种摆放方案使得存在一条只经过直角三角形覆盖区域的$(0,0)$到$(M,M)$的路径的$K$的最小值。$N , M leq 100 , a_i , b_i leq 10^6$
显然是有二分性质的
首先考虑到交换两个直角三角形对答案实际上没有影响,所以拼图的顺序是无所谓的。
所以我们选择DP作为二分的check。设$f_{i,j}$表示前$i$个拼图在横坐标为$j$时最大的纵坐标大小,转移方程用枚举当前三角形直角顶点的位置加上相似推一下就行。
1 #include<bits/stdc++.h>
2 #define LOJ
3 //This code is written by Itst
4 #define ll long long
5 using namespace std;
6
7 inline int read(){
8 int a = 0;
9 bool f = 0;
10 char c = getchar();
11 while(c != EOF && !isdigit(c)){
12 if(c == '-')
13 f = 1;
14 c = getchar();
15 }
16 while(c != EOF && isdigit(c)){
17 a = (a << 3) + (a << 1) + (c ^ '0');
18 c = getchar();
19 }
20 return f ? -a : a;
21 }
22
23 ll tri[101][2] , dp[101] , N , M;
24
25 bool check(int mid){
26 memset(dp , -0x3f , sizeof(dp));
27 dp[0] = 0;
28 for(int i = 1 ; i <= N ; i++)
29 for(int j = M ; j >= 0 ; j--)
30 for(int k = j ; k >= 0 && (j - k) * tri[i][0] <= mid ; k--)
31 dp[j] = max(dp[j] , dp[k] + (mid - (j - k) * tri[i][0]) / tri[i][1]);
32 return dp[M] >= M;
33 }
34
35 int main(){
36 #ifdef LOJ
37 freopen("500.in" , "r" , stdin);
38 //freopen("500.out" , "w" , stdout);
39 #endif
40 N = read();
41 M = read();
42 for(int i = 1 ; i <= N ; i++){
43 tri[i][0] = read();
44 tri[i][1] = read();
45 }
46 int L = 1 , R = 1e8;
47 while(L < R){
48 int mid = L + R >> 1;
49 check(mid) ? R = mid : L = mid + 1;
50 }
51 cout << L;
52 return 0;
53 }