2018.10.17校内模拟赛:T2神光
题面:pdf
首先排序,二分,然后怎么判定是否可行。
最简单的思路是,dp[i][j][k],到第i个,用了j次红光,k次绿光,前i个点都选上了,是否可行。然后转移就行。
然后考试的时候就想到这了,往后没想到。于是贪心,乱搞,和n^3dp拍了几千组随机数据。然后,一共90。其中贪心可得80。
上面的dp中,结果只能是0/1,于是可以考虑减掉一维,让结果表示一维。
n^2 dp:感觉挺奇妙的。f[i][j]红的用了i次,绿的用了j次,最远到什么位置。dp[i][j]=max ( P[dp[i-1][j]+1], Q[dp[i][j-1]+1] )。其中 P[k]表示使用一次红光,能从第 k 座法坛向右(正向为右)连续摧毁到第几座,Q[k]表示使用一次绿光,能从第k座法坛向右连续摧毁到第几座。 P和Q数组可以通过预处理得到。
其他的思路:f[i][j]到i,用了j次红光的时候,最少的绿光次数。
代码:
1 /* 2 * @Author: mjt 3 * @Date: 2018-10-17 16:16:28 4 * @Last Modified by: mjt 5 * @Last Modified time: 2018-10-17 16:40:30 6 */ 7 #include<cstdio> 8 #include<algorithm> 9 #include<cstring> 10 #include<cmath> 11 #include<iostream> 12 #include<cctype> 13 #include<set> 14 #include<vector> 15 #include<queue> 16 #include<map> 17 #define fi(s) freopen(s,"r",stdin); 18 #define fo(s) freopen(s,"w",stdout); 19 using namespace std; 20 typedef long long LL; 21 22 inline int read() { 23 int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1; 24 for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f; 25 } 26 27 const int N = 2005; 28 29 int a[N], P[N], Q[N], f[N][N], n, R, G; 30 31 bool check(int x) { 32 memset(f, 0, sizeof(f)); 33 memset(P, 0, sizeof(P)); 34 memset(Q, 0, sizeof(Q)); 35 for (int i=1; i<=n; ++i) { 36 for (int j=i; j<=n; ++j) { 37 if (a[j] - a[i] + 1 <= x) P[i] = j; 38 if (a[j] - a[i] + 1 <= x + x) Q[i] = j; 39 } 40 } 41 P[n + 1] = Q[n + 1] = n; 42 for (int i=0; i<=R; ++i) 43 for (int j=0; j<=G; ++j) { 44 if (i) f[i][j] = max(f[i][j], P[f[i - 1][j] + 1]); 45 if (j) f[i][j] = max(f[i][j], Q[f[i][j - 1] + 1]); 46 } 47 return f[R][G] == n; 48 } 49 50 int main() { 51 n = read(), R = read(), G = read(); 52 for (int i=1; i<=n; ++i) a[i] = read(); 53 sort(a + 1, a + n + 1); 54 if (R + G >= n) { 55 cout << 1; return 0; 56 } 57 int L = 1, R = a[n] - a[1] + 1, ans = R; 58 while (L <= R) { 59 int mid = (L + R) >> 1; 60 if (check(mid)) ans = mid, R = mid - 1; 61 else L = mid + 1; 62 } 63 cout << ans; 64 return 0; 65 }