学生身高依次是1…N。在合影时候要求每一排从左到右递减,每一列从后面到前也递减,一共有多少总方案
Input
每组测试数据包含两行。第一行给出行数k
第二行包含从后到前(n1,n2,…,nk)的行的长度,作为由单个空格分隔的十进制整数。
问题数据以0结束。
N<=30, k<=5;
Output
输出每组数据的方案数
Sample Input
1 30 5 1 1 1 1 1 3 3 2 1 4 5 3 3 1 5 6 5 4 3 2 2 15 15 0
Sample Output
1 1 16 4158 141892608 9694845
#include#include #include using namespace std; const int N = 31; typedef long long LL; int a[N]; int main() { int k; while (scanf("%d", &k) && k) { for (int i = 0; i < k; i++) { scanf("%d", &a[i]); } for (int i = k; i < 5; i++) a[i] = 0; LL dp[a[0] + 1][a[1] + 1][a[2] + 1][a[3] + 1][a[4] + 1]; memset(dp, 0, sizeof(dp)); dp[0][0][0][0][0] = 1; for (int a1 = 0; a1 <= a[0]; a1++) { for (int a2 = 0; a2 <= a[1]; a2++) { for (int a3 = 0; a3 <= a[2]; a3++) { for (int a4 = 0; a4 <= a[3]; a4++) { for (int a5 = 0; a5 <= a[4]; a5++) { if (a1 < a[0]) { dp[a1 + 1][a2][a3][a4][a5] += dp[a1][a2][a3][a4][a5]; } if (a2 < a[1] && a1 > a2) { dp[a1][a2 + 1][a3][a4][a5] += dp[a1][a2][a3][a4][a5]; } if (a3 < a[2] && a2 > a3 && a1 >= a2) { dp[a1][a2][a3 + 1][a4][a5] += dp[a1][a2][a3][a4][a5]; } if (a4 < a[3] && a3 > a4 && a1 >= a2 && a2 >= a3) { dp[a1][a2][a3][a4 + 1][a5] += dp[a1][a2][a3][a4][a5]; } if (a5 < a[4] && a4 > a5 && a1 >= a2 && a2 >= a3 && a3 >= a4) { dp[a1][a2][a3][a4][a5 + 1] += dp[a1][a2][a3][a4][a5]; } } } } } } printf("%lld\n", dp[a[0]][a[1]][a[2]][a[3]][a[4]]); } return 0; }
Acwing272 最长公共上升子序列
熊大妈的奶牛在小沐沐的熏陶下开始研究信息题目。
小沐沐先让奶牛研究了最长上升子序列,再让他们研究了最长公共子序列,现在又让他们研究最长公共上升子序列了。
小沐沐说,对于两个数列A和B,如果它们都包含一段位置不一定连续的数,且数值是严格递增的,那么称这一段数是两个数列的公共上升子序列,而所有的公共上升子序列中最长的就是最长公共上升子序列了。
奶牛半懂不懂,小沐沐要你来告诉奶牛什么是最长公共上升子序列。
不过,只要告诉奶牛它的长度就可以了。
数列A和B的长度均不超过3000。
输入格式
第一行包含一个整数N,表示数列A,B的长度。
第二行包含N个整数,表示数列A。
第三行包含N个整数,表示数列B。
输出格式
输出一个整数,表示最长公共上升子序列的长度。
数据范围
1≤N≤30001≤N≤3000,序列中的数字均不超过231−1231−1
输入样例:
4
2 2 1 3
2 1 2 3
输出样例:
2
#includeusing namespace std; const int N = 3e3 + 7; int dp[N][N], a[N], b[N], n; int main() { cin >> n; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) cin >> b[i]; for (int i = 1; i <= n; i++) { int val = 0; for (int j = 1; j <= n; j++) { if (a[i] == b[j]) dp[i][j] = val + 1; else dp[i][j] = dp[i - 1][j]; if (b[j] < a[i]) val = max(val, dp[i - 1][j]); } } int ans = 0; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { ans = max(ans, dp[i][j]); } } printf("%d\n", ans); return 0; }
POJ3666 Making the Grade
一条笔直的土路连接着FJ农场上的两个田地,但它改变的海拔比FJ想要的要多。他的牛不介意爬上或下一个斜坡,但他们不喜欢交替的山丘和山谷。FJ想要增加和清除道路上的泥土,这样它就变成了一个单调的斜坡(无论是向上倾斜还是向下倾斜)。 给你N个整数A1, ... , AN (1 ≤ N ≤ 2,000) 描述海拔(0 ≤ Ai ≤ 1,000,000,000) 在路上N个等距的位置,从第一个字段开始,到另一个字段结束。FJ想把这些高度调整成一个新的序列B1, . ... , BN 这要么是不增加的,要么是不减少的。由于增加或清除沿路任何位置的污物所需的费用相同,因此修路的总费用为 | A 1 - B 1| + | A 2 - B 2| + ... + | AN - BN | 请计算他的道路分级的最低费用,使它成为一个连续的斜坡。FJ高兴地告诉你,有符号的32位整数肯定可以用来计算答案。
Input
* Line 1: A single integer: N
* Lines 2..N+1: Line i+1 contains a single integer elevation: Ai
Output
* 第1行:一个整数,它是FJ对其土路进行分级的最小成本,因此它在高程上不会增加或减少。
Sample Input
7 1 3 2 4 5 3 9
Sample Output
3
#include#include #include using namespace std; const int N = 2e3 + 7; const int INF = 0x3f3f3f3f; int a[N], b[N], dp[N][N], n, m; #define isDigit(ch) (ch >= '0' && ch <= '9') #define raw(i) b[i] #define val(x) lower_bound(b + 1, b + 1 + m, x) - b inline int read() { int ans = 0; int f = 1; char ch = getchar(); while (!isDigit(ch)) { if (ch == '-') f = -1; ch = getchar(); } while (isDigit(ch)) ans = (ans << 3) + (ans << 1) + (ch ^ 48), ch = getchar(); return ans * f; } int main() { memset(dp, 0x3f, sizeof(dp)); n = read(); for (int i = 1; i <= n; i++) a[i] = read(), b[i] = a[i]; sort(b + 1, b + 1 + n); m = unique(b + 1, b + 1 + n) - b - 1; dp[0][0] = 0; for (int i = 1; i <= n; i++) { int val = dp[i - 1][0]; for (int j = 1; j <= m; j++) { val = min(val, dp[i - 1][j]); dp[i][j] = min(dp[i][j], val + abs(a[i] - raw(j))); } } int ans = INF; for (int i = 1; i <= m; i++) ans = min(ans, dp[n][i]); printf("%d\n", ans); return 0; }
Acwing274 移动服务
一个公司有三个移动服务员,最初分别在位置1,2,3处。
如果某个位置(用一个整数表示)有一个请求,那么公司必须指派某名员工赶到那个地方去。
某一时刻只有一个员工能移动,且不允许在同样的位置出现两个员工。
从 p 到 q 移动一个员工,需要花费 c(p,q)。
这个函数不一定对称,但保证 c(p,p)=0。
给出N个请求,请求发生的位置分别为 p1p1~pNpN。
公司必须按顺序依次满足所有请求,且过程中不能去其他额外的位置,目标是最小化公司花费,请你帮忙计算这个最小花费。
输入格式
第1行有两个整数L,N,其中L是位置数量,N是请求数量,每个位置从1到L编号。
第2至L+1行每行包含L个非负整数,第i+1行的第j个数表示c(i,j) ,并且它小于2000。
最后一行包含N个整数,是请求列表。
一开始三个服务员分别在位置1,2,3。
输出格式
输出一个整数M,表示最小花费。
数据范围
3≤L≤2003≤L≤200,
1≤N≤10001≤N≤1000
输入样例:
5 9
0 1 1 1 1
1 0 2 3 2
1 1 0 4 1
2 1 5 0 1
4 2 3 4 0
4 2 4 1 5 4 3 2 1
输出样例:
5
#includeusing namespace std; const int N = 1e3 + 7; const int M = 2e2 + 7; const int INF = 0x3f3f3f3f; #define isdigit(ch) (ch >= '0' && ch <= '9') int dp[N][M][M], c[M][M], p[N]; inline int read() { int ans = 0; int f = 1; char c = getchar(); while (!isdigit(c)) { if (c == '-') f = -1; c = getchar(); } while (isdigit(c)) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar(); return ans * f; } int main() { memset(dp, 0x3f, sizeof(dp)); int l = read(), n = read(); dp[0][1][2] = 0; p[0] = 3; for (int i = 1; i <= l; i++) { for (int j = 1; j <= l; j++) { c[i][j] = read(); } } for (int i = 1; i <= n; i++) { p[i] = read(); } for (int i = 1; i <= n; i++) { for (int x = 1; x <= l; x++) { for (int y = 1; y <= l; y++) { if (x == y || x == p[i - 1] || y == p[i - 1]) continue; dp[i][x][y] = min(dp[i][x][y], dp[i - 1][x][y] + c[p[i - 1]][p[i]]); dp[i][p[i - 1]][y] = min(dp[i][p[i - 1]][y], dp[i - 1][x][y] + c[x][p[i]]); dp[i][x][p[i - 1]] = min(dp[i][x][p[i - 1]], dp[i - 1][x][y] + c[y][p[i]]); } } } int ans = INF; for (int x = 1; x <= l; x++) { for (int y = 1; y <= l; y++) { ans = min(ans, dp[n][x][y]); } } printf("%d\n", ans); return 0; }
Acwing275 传纸条
小渊和小轩是好朋友也是同班同学,他们在一起总有谈不完的话题。
一次素质拓展活动中,班上同学安排坐成一个 mm 行 nn 列的矩阵,而小渊和小轩被安排在矩阵对角线的两端,因此,他们就无法直接交谈了。
幸运的是,他们可以通过传纸条来进行交流。
纸条要经由许多同学传到对方手里,小渊坐在矩阵的左上角,坐标(1,1),小轩坐在矩阵的右下角,坐标(m,n)。
从小渊传到小轩的纸条只可以向下或者向右传递,从小轩传给小渊的纸条只可以向上或者向左传递。
在活动进行中,小渊希望给小轩传递一张纸条,同时希望小轩给他回复。
班里每个同学都可以帮他们传递,但只会帮他们一次,也就是说如果此人在小渊递给小轩纸条的时候帮忙,那么在小轩递给小渊的时候就不会再帮忙,反之亦然。
还有一件事情需要注意,全班每个同学愿意帮忙的好感度有高有低(注意:小渊和小轩的好心程度没有定义,输入时用0表示),可以用一个0-100的自然数来表示,数越大表示越好心。
小渊和小轩希望尽可能找好心程度高的同学来帮忙传纸条,即找到来回两条传递路径,使得这两条路径上同学的好心程度之和最大。
现在,请你帮助小渊和小轩找到这样的两条路径。
输入格式
第一行有2个用空格隔开的整数 mm 和 nn,表示学生矩阵有 mm 行 nn 列。
接下来的 mm 行是一个 m∗nm∗n 的矩阵,矩阵中第 ii 行 jj 列的整数表示坐在第 ii 行 jj 列的学生的好心程度,每行的 nn 个整数之间用空格隔开。
输出格式
输出一个整数,表示来回两条路上参与传递纸条的学生的好心程度之和的最大值。
数据范围
1≤n,m≤501≤n,m≤50
输入样例:
3 3
0 3 9
2 8 5
5 7 0
输出样例:
34
#includeusing namespace std; const int N = 1e2 + 7; #define isdigit(ch) (ch >= '0' && ch <= '9') int dp[N][N][N], a[N][N], n, m; inline int read() { int ans = 0; int f = 1; char c = getchar(); while (!isdigit(c)) { if (c == '-') f = -1; c = getchar(); } while (isdigit(c)) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar(); return ans * f; } int main() { n = read(), m = read(); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { a[i][j] = read(); } } for (int i = 2; i <= m + n; i++) { for (int x1 = 1; x1 <= n; x1++) { for (int x2 = 1; x2 <= n; x2++) { int y1 = i - x1; int y2 = i - x2; int w = a[x1][y1]; if (x1 != x2) w += a[x2][y2]; dp[i][x1][x2] = max(dp[i][x1][x2], dp[i - 1][x1][x2] + w); dp[i][x1][x2] = max(dp[i][x1][x2], dp[i - 1][x1][x2 - 1] + w); dp[i][x1][x2] = max(dp[i][x1][x2], dp[i - 1][x1 - 1][x2] + w); dp[i][x1][x2] = max(dp[i][x1][x2], dp[i - 1][x1 - 1][x2 - 1] + w); } } } printf("%d\n", dp[n + m][n][n]); return 0; }
Gym - 100340A Cookies
圣诞老人共有M个饼干,准备全部分给N个孩子。
每个孩子有一个贪婪度,第 i 个孩子的贪婪度为 g[i]。
如果有 a[i] 个孩子拿到的饼干数比第 i 个孩子多,那么第 i 个孩子会产生 g[i]*a[i]的怨气。
给定N、M和序列g,圣诞老人请你帮他安排一种分配方式,使得每个孩子至少分到一块饼干,并且所有孩子的怨气总和最小。
输入格式
第一行包含两个整数N,M。
第二行包含N个整数表示g1g1~gNgN。
输出格式
第一行一个整数表示最小怨气总和。
第二行N个空格隔开的整数表示每个孩子分到的饼干数,若有多种方案,输出任意一种均可。
数据范围
1≤N≤301≤N≤30,
N≤M≤5000N≤M≤5000,
1≤gi≤1071≤gi≤107
输入样例:
3 20
1 2 3
输出样例:
2 2 9 9
#includeusing namespace std; const int N = 37; const int M = 5e3 + 7; struct Node { int g; int id; friend bool operator<(Node a, Node b) { return a.g > b.g; } }a[N]; int n, m, dp[N][M], sum[N], ans[N]; int main() { freopen("cookies.in", "r", stdin); freopen("cookies.out", "w", stdout); scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d", &a[i].g); a[i].id = i; } sort(a + 1, a + 1 + n); for (int i = 1; i <= n; i++) { sum[i] = sum[i - 1] + a[i].g; } memset(dp, 0x3f, sizeof(dp)); dp[0][0] = 0; for (int i = 1; i <= n; i++) { for (int j = 0; j <= m; j++) { int res = 0x3f3f3f3f; for (int k = 0; k < i; k++) { if (j < i - k) continue; int temp = dp[k][j - (i - k)] + k * (sum[i] - sum[k]); res = min(res, temp); } if (j >= i) res = min(dp[i][j - i], res); dp[i][j] = res; } } printf("%d\n", dp[n][m]); int i = n, j = m, h = 0; while (i && j) { if (j >= i && dp[i][j] == dp[i][j - i]) j -= i, h++; else { for (int k = 1; k <= min(i, j); k++) { if (dp[i][j] == dp[i - k][j - k] + (sum[i] - sum[i - k]) * (i - k)) { for (int u = i; u > i - k; u--) { ans[a[u].id] = h + 1; } i -= k, j -= k; break; } } } } for (int k = 1; k <= n; k++) printf("%d%c", ans[k], k == n ? '\n' : ' '); return 0; }