做了这么多的笔试编程题,觉得这次的笔试题很有质量,两个动态规划,其中一个还是一个简单的区间动态规划(笔试几乎不考,不是说”最难不过二叉树”吗)。
原题链接:点这儿.
A、B两伙马贼意外地在一片沙漠中发现了一处金矿,双方都想独占金矿,但各自的实力都不足以吞下对方,经过谈判后,双方同意用一个公平的方式来处理这片金矿。处理的规则如下:他们把整个金矿分成
n
段,由A、B开始轮流从最左端或最右端占据一段,直到分完为止。
马贼A想提前知道他们能分到多少金子,因此请你帮忙计算他们最后各自拥有多少金子?(两伙马贼均会采取对己方有利的策略)
如果这个题能保证n
为偶数,那么这是一个博弈题,可以找到奇异状态。因为如果是偶数,那么我先手,我总是能控制自己拿奇数位上的金子或者是偶数位上的金子,只要一开始算一下是奇数位上金子的总数多还是偶数为上金子的总数多。哪个多,就拿哪个。
但是,这题中n
可以为奇数,因此,只能区间动态规划了。
设 d p i , j dp_{i,j} dpi,j表示区间 [ i , j ] [i,j] [i,j]能拿到的最多金子数,那么状态转移方程为
d p i , j = s u m [ i , j ] − m i n { d p i + 1 , j , d p i , j − 1 } dp_{i,j} = sum[i,j] - min\{dp_{i + 1, j}, dp_{i, j - 1}\} dpi,j=sum[i,j]−min{dpi+1,j,dpi,j−1}
只有这样子才符合两伙马贼均会采取对己方有利的策略。
#include
using namespace std;
int main()
{
int T, K = 1;
for (cin >> T; T--; K++) {
int n;
vector<int> arr((cin >> n, n + 1), 0), sum(arr);
for (int i = 1; i <= n; i++)
sum[i] = sum[i - 1] + (cin >> arr[i], arr[i]);
vector<vector<int> > dp(n + 1, vector<int>(n + 1, 0));
for (int i = 0; i <= n; i++)
dp[i][i] = arr[i];
for (int L = 2; L <= n; L++) {
for (int l = 1; l + L - 1 <= n; l++) {
int r = l + L - 1;
dp[l][r] = sum[r] - sum[l - 1] - min(dp[l + 1][r], dp[l][r - 1]);
}
}
printf("Case #%d: %d %d\n", K, dp[1][n], sum[n] - dp[1][n]);
}
return 0;
}
小明买了一些彩色的气球用绳子串在一条线上,想要装饰房间,每个气球都染上了一种颜色,每个气球的形状都是各不相同的。我们用1到9一共9个数字表示不同的颜色,如12345则表示一串5个颜色各不相同的气球串。但小明希望得到不出现重复颜色的气球串,那么现在小明需要将这个气球串剪成多个较短的气球串,小明一共有多少种剪法?如原气球串12345的一种是剪法是剪成12和345两个气球串。
注意每种剪法需满足最后的子串中气球颜色各不相同(如果满足该条件,允许不剪,即保留原串)。两种剪法不同当且仅当存在一个位置,在一种剪法里剪开了,而在另一种中没剪开。详见样例分析。
也是动态规划,从后面开始分析,设 d p i dp_i dpi是区间 [ 0 , i ] [0,i] [0,i]的答案,那么从 i i i开始向前到 i − 1 , i − 2 , . . . i - 1, i-2,... i−1,i−2,...,只要 [ i − j , i ] [i-j, i] [i−j,i]这段区间的颜色不重复就可以剪断。那么状态转移方程是:
d p i = 1 , d p i + = ∑ j = 1 10 d p i − j dp_i=1, dp_i += \sum_{j=1}^{10}{dp_{i-j}} dpi=1,dpi+=j=1∑10dpi−j
#include
using namespace std;
typedef long long LL;
const int mod = 1000000007;
int main()
{
int n;
while (cin >> n) {
vector<int> arr(n);
for (int i = 0; i < n; cin >> arr[i++]);
vector<LL> dp(n, 0);
dp[0] = 1;
for (int i = 1; i < n; i++) {
bool used[10] = {false};
for (int j = i; j >= 0; j--) {
if (used[arr[j]])
break;
used[arr[j]] = true;
dp[i] = (dp[i] + (j > 0 ? dp[j - 1] : 1)) % mod;
}
}
cout << dp[n - 1] << endl;
}
return 0;
}
小明同学喜欢体育锻炼,他常常去操场上跑步。跑道是一个圆形,在本题中,我们认为跑道是一个半径为
R
的圆形,设圆心的坐标为原点(0,0)
。
小明跑步的起点坐标为(R,0)
,他沿着圆形跑道跑步,而且一直沿着一个方向跑步。回到家后,他查看了自己的计步器,计步器显示他跑步的总路程为L
。
小明想知道自己结束跑步时的坐标,但是他忘记自己是沿着顺时针方向还是逆时针方向跑的了。他想知道在这两种情况下的答案分别是多少。
画个图,算算三角函数就知道了。
#include
using namespace std;
const double PI = 4 * atan(1.0);
int main()
{
double L, R;
while (cin >> L >> R) {
double theta = L / R;
double x = R * cos(theta), y = R * sin(theta);
printf("%.3f %.3f\n%.3f %.3f\n", x, -y, x, y);
}
return 0;
}