今天按照计划的安排,如期进行了动态规划有关内容的测试。
分数: 80
前言
其实动态规划这天我是挺慌的,因为当初学动归的时候没有任何的基础,其实讲的时候好多基础题都是没思路的,不过还好,有一个系统的学习的机会,把几个基本的动归问题听懂之后,就慢慢的开始有了思路。这算是正式入门了吧。
T1
今天的T1其实… 怎么说,是一道很不像DP的一道题。题目是关于排列数的, 其实昨天有讲过这种DP的写法, 但是我感觉好像并找不到什么思路, 于是我就先打了一个暴力试了试, 就是生成了所有的排列, 然后将满足条件的记一下数, 其实就是先打一个表, 结果发现答案都很有规律, 可以推出一个公式即 2 ^ (n - 1) % p . 发现公式的我欣喜若狂, 于是这就成了一个考快速幂板子的题了。 需要注意的是在快速幂中的相乘取模需要用到快速乘。 于是就可以AC了
标程:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// # include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 5e5 + 10;
const int mod = 1e9+7;
int T;
long long n, p;
inline ll Mul(ll a, ll b) { // 快速乘
ll ans = 0;
while(b) {
if(b&1) ans += a, ans %= p;
a <<= 1, a %= p;
b >>= 1;
}
return ans;
}
inline ll Pow(ll b) { // 快速幂
if(p == 1) return 0ll;
ll ans = 1, a = 2ll;
while(b) {
if(b&1) ans = Mul(ans, a), ans %= p;
a = Mul(a, a);
b >>= 1;
}
return ans;
}
int main() {
// freopen("jian.in", "r", stdin);
// freopen("jian.out", "w", stdout);
scanf("%d", &T);
while(T--) {
cin >> n >> p;
cout << Pow(n-1) << endl; // 直接带公式
}
return 0;
}
可以看到老师的代码缩进很好看, 而且有一点是, 老师对于常量的使用没有使用 #define, 而是使用 const 定义,ll 的缩写也没有使用 #define, 而是使用了 typedef 来定义 ll 的缩写, 这些代码风格都可以借鉴与学习。
突然发现自己的程序好像比老师的程序快了几秒, 貌似就是读入优化的问题?貌似可以显著的提高读入的速度,可以避免被卡常数吧.. 表示测的3遍, 两遍因为卡常被卡掉了最后一组数据, 简化代码和尽量减少无用的操作是优化常数的一个很好的方法。
T2
这道题是一道树形DP, 其实我的思路应该是暴力思路, 拿 60 分的那种, 但是在打 DP 求树的直径的时候打炸掉了, 应该得到的暴力分没有拿到,(其实听说好像不用DP求直径, DFS就可以处理出树的直径)
正解思路:
先预处理出一条直径(𝑎,𝑏),如果边不在直径上,那么这次答案就是直径了。
如果枚举的是直径上的边,我们考虑对于a和b为根分别dp出𝑓1_𝑥,𝑓2_𝑥表示以a/b为根,x子树直径大小是多少。(这可以用树形dp处理,记录最大值&次大值)(注意这里思想的转化,不用枚举每一条边,只需要考虑直径上的边即可,这样可以大幅度的降低时间复杂度)
然后枚举每一条直径上的边,调用查询即可。
时间复杂度𝑂(𝑇𝑛)
标程:
1 |
|
老师的程序里出现了关于指针的使用, 在日常打题的时候要避免使用指针。
T3
直接贴题解了, 概率dp课件留着, 之后理解。
标程:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 50 + 10;
const int INF = 1e9;
namespace work {
bool f[M][M][M*M];
int n, a[M], b[M], len[M], m, X;
void main() {
memset(f, 0, sizeof f);
int pre = 0;
cin >> n; m = 0;
for (int i=1; i<=n; ++i) {
scanf("%d", &a[i]);
m += (a[i] == 0);
}
for (int i=1; i<=n; ++i) scanf("%d", &b[i]);
cin >> X;
sort(a+1, a+n+1);
sort(b+1, b+n+1);
for (int i=1; i<n; ++i) len[i] = b[i+1] - b[i] - 1;
len[n] = X - b[n];
for (int i=1; i<=n; ++i) {
if(a[i] == 0) continue;
int tem = 0;
for (int j=1; j<=n; ++j)
if(a[i] > b[j]) ++tem;
if(tem) len[tem] --;
pre += tem;
}
for (int j=0; j<=m; ++j) f[0][j][pre] = 1;
for (int i=1; i<=n; ++i)
for (int j=0; j<=m; ++j)
for (int k=0; k<=n*n; ++k)
for (int cur=0, to = min(j, len[i]); cur<=to; ++cur)
if(k-i*cur >= 0) f[i][j][k] |= f[i-1][j-cur][k-i*cur];
double ans = 1e9, E = (double)n*n, id;
for (int i=0; i<=n*n; ++i) {
if(f[n][m][i]) {
if(fabs(2*i-E) < ans) {
ans = fabs(2*i-E);
id = i;
}
}
}
printf("%.10lf\n", id/n/n);
}
}
int main() {
freopen("ti.in", "r", stdin);
freopen("ti.out", "w", stdout);
work :: main();
return 0;
}
总结
别人的故事, 已经听了许多, 终于要自己把握机会, 尝试一下了吧。 如果这次错过, 输掉的将不止一场比赛, 失去的将会更多! 加油!