小白书动规书后习题:
UVA 116
简单记忆化dp,一个点可以更新三个点
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 100 + 5;
#define LL long long
#define inf (1000000007)
LL dp[MAXN][MAXN];
int data[MAXN][MAXN];
int path[MAXN][MAXN];
int out[MAXN];
int main()
{
// freopen("UVA 116.in", "r", stdin);
int n, m;
while(scanf("%d%d", &n, &m) != EOF && n + m){
for(int i = 1 ; i <= n ; i++) for(int j = 1 ; j <= m ; j++) scanf("%d", &data[i][j]);
memset(dp, 0, sizeof(dp));
for(int j = m ; j >= 1 ; j--){
for(int i = 1 ; i <= n ; i++){
if(j == m) dp[i][j] = data[i][j], path[i][j] = -2;
else{
dp[i][j] = inf;
if(i == n && dp[i][j] > dp[1][j + 1]) dp[i][j] = dp[1][j + 1], path[i][j] = 1 - n;
if(i > 1 && dp[i][j] > dp[i - 1][j + 1]) dp[i][j] = dp[i - 1][j + 1], path[i][j] = -1;
if(dp[i][j] > dp[i][j + 1]) dp[i][j] = dp[i][j + 1], path[i][j] = 0;
if(i < n && dp[i][j] > dp[i + 1][j + 1]) dp[i][j] = dp[i + 1][j + 1], path[i][j] = 1;
if(i == 1 && dp[i][j] > dp[n][j + 1]) dp[i][j] = dp[n][j + 1], path[i][j] = n - 1;
dp[i][j] += data[i][j];
}
}
}
// printf("dp\n");
// for(int i = 1 ; i <= n ; i++){
// for(int j = 1 ; j <= m ; j++)
// printf("%lld ", dp[i][j]);
// printf("\n");
// }
// printf("dp\n");
// printf("path\n");
// for(int i = 1 ; i <= n ; i++){
// for(int j = 1 ; j <= m ; j++)
// printf("%d ", path[i][j]);
// printf("\n");
// }
// printf("path\n");
LL ans = dp[1][1];
int mark = 1;
for(int i = 2 ; i <= n ; i++){
if(ans > dp[i][1]) ans = dp[i][1], mark = i;
}
int cnt = 1;
while(cnt <= m){
// printf("cnt = %d, mark = %d\n", cnt, mark);
out[cnt] = mark;
mark = mark + path[mark][cnt++];
}
for(int i = 1 ; i < cnt ; i++){
printf("%d", out[i]);
if(i == cnt - 1) printf("\n");
else printf(" ");
}
printf("%lld\n", ans);
}
return 0;
}
UVA 10066
裸的最长公共子序列
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
const int MAXN = 100 + 5;
int s1[MAXN], s2[MAXN];
int dp[MAXN][MAXN];
int main()
{
int n, m;
int cas = 0;
while(scanf("%d%d", &n, &m) != EOF && n + m){
for(int i = 1 ; i <= n ; i++) scanf("%d", &s1[i]);
for(int i = 1 ; i <= m ; i++) scanf("%d", &s2[i]);
memset(dp, 0, sizeof(dp));
for(int i = 1 ; i <= n ; i++){
for(int j = 1 ; j <= m ; j++){
if(s1[i] == s2[j]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
printf("Twin Towers #%d\nNumber of Tiles : %d\n\n", ++cas, dp[n][m]);
}
return 0;
}
UVA 10131
最长上升子序列,放在结构里面按照价值先排个序,然后找最长的上升子序列就行。
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 1000 + 5;
struct D
{
int u,v;
int mark;
}d[MAXN];
bool cmp(D a, D b){return a.u < b.u;}
int dp[MAXN], path[MAXN];
int out[MAXN];
int main()
{
int u, v, cnt = 0;
while(scanf("%d%d", &u, &v) != EOF)
d[cnt].mark = cnt + 1, d[cnt].u = u, d[cnt++].v = v;
sort(d, d + cnt, cmp);
for(int i = 0 ; i < cnt ; i++) dp[i] = 1, path[i] = i;
for(int i = 0 ; i < cnt ; i++){
for(int j = i + 1 ; j < cnt ; j++){
if(d[i].u < d[j].u && d[i].v > d[j].v){
if(dp[j] < dp[i] + 1) dp[j] = dp[i] + 1, path[j] = i;
}
}
}
int ans = dp[0];
int mark = 0;
for(int i = 1 ; i < cnt ; i++)
if(ans < dp[i]) ans = dp[i], mark = i;
cnt = 0;
while(mark != path[mark])
out[cnt++] = mark, mark = path[mark];
out[cnt++] = mark;
printf("%d\n", ans);
for(int i = cnt - 1 ; i >= 0 ; i--){
printf("%d", d[out[i]].mark);
// if(i) printf(" ");
printf("\n");
}
return 0;
}
UVA 10192
裸的最长公共子序列。然而不明白加了答案是1时为什么不能特判输出语句为city。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <string>
using namespace std;
const int MAXN = 100 + 5;
char s1[MAXN], s2[MAXN];
int dp[MAXN][MAXN];
int main()
{
int cas = 0;
while(1){
cin.getline(s1, MAXN);
if(s1[0] == '#') break;
cin.getline(s2, MAXN);
int l1 = strlen(s1), l2 = strlen(s2);
memset(dp, 0, sizeof(dp));
for(int i = 1 ; i <= l1 ; i++){
for(int j = 1 ; j <= l2 ; j++){
if(s1[i - 1] == s2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
// if(dp[l1][l2] != 1)
printf("Case #%d: you can visit at most %d cities.\n", ++cas, dp[l1][l2]);
// else
// printf("Case #%d: you can visit at most %d city.\n", ++cas, dp[l1][l2]);
}
return 0;
}
UVA 147
裸的整数分化,只不过分化的种类略多。
有整数筛和记忆化+递归的写法,后者比较好写。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
#define LL long long
const int MAXN = 6000 + 5;
int data[11] = {1, 2, 4, 10, 20, 40, 100, 200, 400, 1000, 2000};
LL dp[MAXN][11];
char str[10];
LL solve(LL num, int mark)
{
if(dp[num][mark] != -1) return dp[num][mark];
dp[num][mark] = 0;
for(int i = 0 ; i <= mark ; i++){
if(num >= data[i]){
dp[num][mark] += solve(num - data[i], i);
// if(num == 4) printf("i = %d, dp = %lld\n", i, solve(num - data[i], i));
}
else break;
}
return dp[num][mark];
}
int main()
{
memset(dp, -1, sizeof(dp));
for(int i = 0 ; i < 11 ; i++){
dp[0][i] = 1;
}
while(scanf("%s", str) != EOF){
int temp = 0;
int f = 1, cnt = 0;
for(int i = 0 ; i < strlen(str) ; i++){
if(str[i] == '.') {f = 0;continue;}
if(f == 0) cnt++;
temp = temp * 10 + str[i] - '0';
}
while(cnt < 2) cnt++, temp *= 10;
temp /= 5;
if(temp == 0) break;
// printf("temp = %d\n", temp);
LL ans = solve(temp, 10);
printf("%6s%17lld\n", str, ans);
}
return 0;
}
UVA 357
一样的题……
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
const int MAXN = 30000 + 5;
LL dp[MAXN][5];
int d[5] = {1, 5, 10, 25, 50};
LL solve(int num, int mark)
{
if(dp[num][mark] != -1) return dp[num][mark];
dp[num][mark] = 0;
for(int i = 0 ; i <= mark ; i++){
if(num >= d[i]) dp[num][mark] += solve(num - d[i], i);
else break;
}
return dp[num][mark];
}
int main()
{
memset(dp, -1, sizeof(dp));
for(int i = 0 ; i < 5 ; i++)
dp[0][i] = 1;
int n;
while(scanf("%d", &n) != EOF){
if(n == 0){
printf("There is only 1 way to produce 0 cents change.\n");
continue;
}
LL ans = solve(n, 4);
if(ans == 1)
printf("There is only 1 way to produce %d cents change.\n", n);
else
printf("There are %lld ways to produce %d cents change.\n", ans, n);
}
return 0;
}
UVA 10003
这道题不会写,所以写一下题解。
设dp[i][j]是以i到j为木棍切割点时最少消耗。然后就枚举一下是先切割了中间的哪一个点,然后递归处理dp[i][k]和dp[k][j]即可。其实就是简单的区间DP。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstdlib>
using namespace std;
const int MAXN = 50 + 5;
int dp[MAXN][MAXN];
int d[MAXN];
int solve(int l, int r)
{
if(dp[l][r] != -1) return dp[l][r];
dp[l][r] = solve(l, l + 1) + solve(l + 1, r) + d[r] - d[l];
for(int i = l + 2 ; i < r ; i++)
dp[l][r] = min(dp[l][r], solve(l, i) + solve(i, r) + d[r] - d[l]);
return dp[l][r];
}
int main()
{
int l, n;
while(scanf("%d", &l) != EOF && l){
scanf("%d", &n);
memset(dp, -1, sizeof(dp));
d[0] = 0;
for(int i = 1 ; i <= n ; i++){
scanf("%d", &d[i]), dp[i - 1][i] = 0;
}
dp[n][n + 1] = 0;
d[++n] = l;
printf("The minimum cutting is %d.\n", solve(0, n));
}
return 0;
}
UVA 562
背包问题,看最后组成方案中最接近且小于sum/2的值是多少,转化一下即可出答案。
sum表示所有硬币值得和。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 50000 + 5;
int d[MAXN];
int dp[MAXN];
int main()
{
int t;
scanf("%d", &t);
while(t--){
int n;
scanf("%d", &n);
int sum = 0;
for(int i = 0 ; i < n ; i++) scanf("%d", &d[i]), sum += d[i];
memset(dp, -1, sizeof(dp));
dp[0] = 1;
for(int i = 0 ; i < n ; i++){
for(int j = sum / 2 ; j >= d[i] ; j--){
if(dp[j - d[i]] != -1) dp[j] = 1;
}
}
// for(int i = 0 ; i <= sum / 2 ; i++)
// printf("dp[%d] = %d\n", i, dp[i]);
int ans = sum;
for(int i = sum / 2 ; i >= 0 ; i--){
if(dp[i] != -1){
ans = sum - 2 * i;
break;
}
}
printf("%d\n", ans);
}
return 0;
}
UVA 348
石子合并问题类似,难点主要在于输出(大模拟)
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
#define LL long long
const int MAXN = 10 + 2;
int r[MAXN], c[MAXN];
LL dp[MAXN][MAXN];
int pri[MAXN];
int path[MAXN][MAXN];
void solve(int l, int r)
{
int mark = path[l][r];
// printf("\nl = %d, r = %d, mark = %d\n", l, r, mark);
// system("pause");
if(l == r){
printf("A%d", l);
return;
}
if(mark - l > 0) printf("(");
solve(l, mark);
if(mark - l > 0) printf(")");
printf(" x ");
if(r - mark - 1 > 0) printf("(");
solve(mark + 1, r);
if(r - mark - 1 > 0) printf(")");
}
int main()
{
// freopen("UVA 348.in","r", stdin);
int n;
int cas = 0;
while(scanf("%d", &n) != EOF && n){
for(int i = 1 ; i <= n ; i++)
scanf("%d%d", &r[i], &c[i]);
memset(dp, -1, sizeof(dp));
for(int i = 1 ; i < n ; i++)
dp[i][i + 1] = r[i] * c[i] * c[i + 1], dp[i][i] = 0, path[i][i + 1] = i;
for(int k = 2 ; k <= n ; k++){
for(int i = 1 ; i + k <= n ; i++){
dp[i][i + k] = dp[i + 1][i + k] + r[i] * c[i] * c[i + k];
path[i][i + k] = i;
for(int j = i + 1 ; j < i + k ; j++){
int temp = dp[i][j] + dp[j + 1][i + k] + r[i] * c[j] * c[i + k];
if(dp[i][i + k] > temp) path[i][i + k] = j, dp[i][i + k] = temp;
}
}
}
printf("Case %d: ", ++cas);
printf("(");
solve(1, n);
printf(")");
printf("\n");
}
return 0;
}
UVA 624
题目意思有点难理解,要求选取轨道的值比预计值N小且最接近N。有多种时选取用轨道最多的那种方案。
因为数据比较小而且没有给时间的上限,所以用状压DP做的。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
const int MAXN = 20 + 2;
int dp[1 << MAXN];
int d[MAXN];
int out[MAXN];
int main()
{
int n, m;
while(scanf("%d", &n) != EOF && n){
scanf("%d", &m);
for(int i = 0 ; i < m ; i++) scanf("%d", &d[i]);
int sum = 0;
int cnt = 0;
int ans = 0;
for(int i = 1 ; i < (1 << m) ; i++){
if(dp[i] == -1) continue;
int tsum = 0, tans = 0;
for(int j = 0 ; j < m ; j++){
if(i & (1 << j)) tsum += d[j], tans++;
}
// printf("i = %d, tsum = %d, tans = %d\n", i, tsum, tans);
if(n - tsum >= 0 && n - tsum < n - sum)
ans = i, cnt = tans, sum = tsum;
else if(n - tsum == n - sum && cnt < tans)
cnt = tans, ans = i;
}
cnt = 0;
for(int i = 0 ; i < m ; i++)
if(ans & (1 << i)) out[cnt++] = i;
for(int i = 0 ; i < cnt ; i++)
printf("%d ", d[out[i]]);
printf("sum:%d\n", sum);
}
return 0;
}
UVA 10130
裸的01背包
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 1000 + 5;
int dp[MAXN * 30];
int w[MAXN], v[MAXN];
int main()
{
int t;
scanf("%d", &t);
while(t--){
int n;
scanf("%d", &n);
int up = 0;
for(int i = 0 ; i < n ; i++) scanf("%d%d", &v[i], &w[i]), up += w[i];
memset(dp, 0, sizeof(dp));
dp[0] = 0;
for(int i = 0 ; i < n ; i++){
for(int j = up ; j >= w[i] ; j--){
dp[j] = max(dp[j], v[i] + dp[j - w[i]]);
}
}
int ans = 0;
int q;
scanf("%d", &q);
while(q--){
scanf("%d", &up);
ans += dp[up];
}
printf("%d\n", ans);
}
return 0;
}
UVA 531
最长公共子序列,用的string处理每个字符串。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
const int MAXN = 3500;
const int MAXM = 200;
string s1[MAXN], s2[MAXN];
int dp[MAXM][MAXM], path[MAXM][MAXM];
string ans[MAXN * 2];
int cnt;
void solve(int cnt1, int cnt2)
{
if(cnt1 == 0 || cnt2 == 0) return;
if(path[cnt1][cnt2] == 0) solve(cnt1 - 1, cnt2 - 1);
else if(path[cnt1][cnt2] == 1) solve(cnt1 - 1, cnt2);
else if(path[cnt1][cnt2] == -1) solve(cnt1, cnt2 - 1);
if(path[cnt1][cnt2] == 0) ans[cnt++] = s1[cnt1];
}
int main()
{
int cnt1, cnt2;
cnt1 = cnt2 = 0;
while(cin>>s1[++cnt1] != NULL){
if(s1[cnt1][0] != '#')
while(cin>>s1[++cnt1] && s1[cnt1][0] != '#');
while(cin>>s2[++cnt2] && s2[cnt2][0] != '#');
cnt1--, cnt2--;
memset(dp, 0, sizeof(dp));
for(int i = 1 ; i <= cnt1; i ++){
for(int j = 1 ; j <= cnt2 ; j++){
if(s1[i] == s2[j]) dp[i][j] = dp[i - 1][j - 1] + 1, path[i][j] = 0;
else if(dp[i][j - 1] > dp[i - 1][j]) dp[i][j] = dp[i][j - 1], path[i][j] = -1;
else dp[i][j] = dp[i - 1][j], path[i][j] = 1;
}
}
cnt = 0;
solve(cnt1, cnt2);
for(int i = 0 ; i < cnt ; i++){
cout << ans[i];
if(i == cnt - 1) printf("\n");
else printf(" ");
}
cnt1 = cnt2 = 0;
}
return 0;
}
UVA 10456
只有两个物品的完全背包
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 10000 + 5;
int dp[MAXN];
int main()
{
int n, m, t;
while(scanf("%d%d%d", &n, &m, &t) != EOF){
memset(dp, -1, sizeof(dp));
dp[0] = 0;
for(int i = 0 ; i < MAXN ; i++){
if(i + n > t) break;
if(dp[i] != -1){
dp[i + n] = max(dp[i + n], dp[i] + 1);
}
}
for(int i = 0 ; i < MAXN ; i++){
if(i + m > t) break;
if(dp[i] != -1){
dp[i + m] = max(dp[i + m], dp[i] + 1);
}
}
int ans = 0;
int beer = t;
int ok = 0;
int ok2 = 0;
for(int i = t ; i >= 0 ; i--){
if(dp[i] != -1){
ans = dp[i];
if(i == t) ok = 1;
ok2 = 1;
beer = t - i;
break;
}
}
if(ok == 1)
printf("%d\n", ans);
else
printf("%d %d\n", ans, beer);
}
return 0;
}
UVA 10285
记忆化+dp
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 100 + 5;
int g[MAXN][MAXN], dp[MAXN][MAXN];
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, -1, 0, 1};
char name[MAXN];
int n, m;
int valid(int x, int y)
{
if(x < 1 || x > n) return false;
if(y < 1 || y > m) return false;
return true;
}
int solve(int x, int y)
{
if(dp[x][y] != -1) return dp[x][y];
dp[x][y] = 1;
for(int i = 0 ; i < 4 ; i++){
int tx = dx[i] + x;
int ty = dy[i] + y;
if(valid(tx, ty) && g[tx][ty] > g[x][y])
dp[x][y] = max(dp[x][y], solve(tx, ty) + 1);
}
return dp[x][y];
}
int main()
{
int t;
scanf("%d", &t);
while(t--){
scanf("%s", name);
scanf("%d%d", &n, &m);
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= m ; j++) scanf("%d", &g[i][j]);
memset(dp, -1, sizeof(dp));
int ans = 1;
for(int i = 1 ; i <= n ; i++)
for(int j = 1 ; j <= m ; j++)
ans = max(ans, solve(i, j));
printf("%s: %d\n", name, ans);
}
return 0;
}
UVA 437
排序后往后DP即可。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
using namespace std;
const int MAXN = 30 * 6;
int dp[MAXN];
struct D
{
int x, y, z;
D(){}
D(int _x, int _y, int _z){x = _x, y = _y, z = _z;}
}d[MAXN];
bool cmp(D a, D b)
{
if(a.x != b.x) return a.x > b.x;
if(a.y != b.y) return a.y > b.y;
return a.z > b.z;
}
int main()
{
// freopen("UVA 437.in", "r", stdin);
int n;
int cas = 0;
while(scanf("%d", &n) != EOF && n){
for(int i = 0 ; i < n ; i++){
scanf("%d%d%d", &d[i * 6].x, &d[i * 6].y, &d[i * 6].z);
d[i * 6 + 1] = D(d[i * 6].x, d[i * 6].z, d[i * 6].y);
d[i * 6 + 2] = D(d[i * 6].y, d[i * 6].x, d[i * 6].z);
d[i * 6 + 3] = D(d[i * 6].y, d[i * 6].z, d[i * 6].x);
d[i * 6 + 4] = D(d[i * 6].z, d[i * 6].x, d[i * 6].y);
d[i * 6 + 5] = D(d[i * 6].z, d[i * 6].y, d[i * 6].x);
}
n *= 6;
sort(d, d + n, cmp);
for(int i = 0 ; i < n ; i++) dp[i] = 0;
int ans = 0;
for(int i = 0 ; i < n ; i++){
dp[i] += d[i].z;
ans = max(ans, dp[i]);
for(int j = i + 1 ; j < n ; j++){
if(d[i].x > d[j].x && d[i].y > d[j].y)
dp[j] = max(dp[i], dp[j]);
}
}
// printf("/////*****n = %d***////\n", n);
// for(int i = 0 ; i < n ; i++)
// printf("x = %d, y = %d, z = %d, dp[%d] = %d\n", d[i].x, d[i].y, d[i].z, i, dp[i]);
printf("Case %d: maximum height = %d\n", ++cas, ans);
}
return 0;
}
UVA 825
题面好奇怪。
就是一个地图有坏点不能走,问从(1,1)走到(n,m)的方案数。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
const int MAXN = 1000 + 5;
long long dp[MAXN][MAXN];
int g[MAXN][MAXN];
char s[MAXN];
int main()
{
int t;
scanf("%d", &t);
int f = 1;
while(t--){
int n, m;
scanf("%d%d", &n, &m);
memset(dp, 0, sizeof(dp));
memset(g, 0, sizeof(g));
for(int i = 1; i <= n ; i++){
int u;
scanf("%d", &u);
cin.getline(s, MAXN);
int temp = 0;
for(int j = 0 ; j < (int)strlen(s) ; j++){
if(s[j] == ' '){
g[u][temp] = -1;
temp = 0;
}
else temp = temp * 10 + s[j] - '0';
}
if(temp != 0) g[u][temp] = -1;
}
if(f) f = 0;
else printf("\n");
if(g[1][1] == -1 || g[n][m] == -1){
printf("0\n");
continue;
}
dp[1][1] = 1;
for(int i = 1 ; i <= n ; i++){
for(int j = 1 ; j <= m ; j++){
if(g[i][j] == -1) continue;
dp[i][j + 1] += dp[i][j];
dp[i + 1][j] += dp[i][j];
}
}
printf("%lld\n", dp[n][m]);
}
return 0;
}
UVA 10069
dp[i]表示表示在字符串Z中到i结尾的种类多少个。
按照顺序遍历S字符串,按照倒序遍历Z字符串,就把问题转化成一个类似于01背包的问题。
作死用java写的,主要是字符串处理。String.AtChar(int pos)可以访问第i位的字符,cin.NextLine()可以完成输出。
交文件的时候把package注释掉,第一个public class后面接Main
//package uva10069;
import java.io.*;
import java.math.*;
import java.text.*;
import java.util.*;
//import java.io.BufferedReader;
//import java.io.IOException;
//import java.io.InputStreamReader
public class Main {
public static void main(String[] args)
{
Scanner cin = new Scanner(new BufferedInputStream(System.in));
// BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));
int t;
t = cin.nextInt();
String s1 = cin.nextLine();
while(t > 0){
t--;
s1 = cin.nextLine();
String s2 = cin.nextLine();
int l1 = s1.length();
int l2 = s2.length();
if(l2 == 0){
System.out.println("1\n");
continue;
}
// char[] s1 = t1.charAt(0);
// char[] s2 = t2.charAt(0);
BigInteger[] dp;
dp = new BigInteger[1000];
BigInteger one = BigInteger.valueOf(1);
for(int i = 0 ; i < l2 ; i++)
dp[i] = BigInteger.valueOf(0);
// System.out.println("t1 = " + t1 + "\nt2 = " + t2 + "\n");
// System.out.println("s1 = " + s1 + "\ns2 = " + s2 + "\n");
for(int i = 0 ; i < l1 ; i++){
for(int j = l2 - 1 ; j >= 0 ; j--){
if(s1.charAt(i) == s2.charAt(j)){
// System.out.println("i = " + i + " j = " + j + "\n");
if(j == 0) dp[j] = dp[j].add(one);
else dp[j] = dp[j].add(dp[j - 1]);
}
}
}
// for(int i = 0 ; i < l1 ; i++){
// System.out.println("s1.Atchar[" + i + "] = " + s1.charAt(i) + "\n");
// }
// for(int i = 0 ; i < l2 ; i++)
// System.out.println("dp[" + i + "] = " + dp[l2 - 1] + "\n");
System.out.println(dp[l2 - 1]);
// System.out.println("\n");
}
}
}
UVA 10534
最长上升子序列的变形。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <string>
using namespace std;
const int MAXN = 10000 + 5;
int dpl[MAXN], dpr[MAXN];
int d[MAXN];
int l[MAXN], r[MAXN];
int main()
{
int n;
while(scanf("%d", &n) != EOF){
for(int i = 0 ; i < n ; i++) scanf("%d", &d[i]);
int cnt = 0;
l[0] = 0;
for(int i = 0 ; i < n ; i++){
if(d[i] > l[cnt]){
l[++cnt] = d[i];
dpl[i] = cnt;
}
else{
int mark = lower_bound(l, l + cnt, d[i]) - l;
l[mark] = min(l[mark], d[i]);
dpl[i] = mark;
}
}
cnt = 0;
for(int i = n - 1 ; i >= 0 ; i--){
if(d[i] > l[cnt]){
l[++cnt] = d[i];
dpr[i] = cnt;
}
else{
int mark = lower_bound(l, l + cnt, d[i]) - l;
l[mark] = min(l[mark], d[i]);
dpr[i] = mark;
}
}
// printf("dpl\n");
// for(int i = 0 ; i < n ; i++)
// printf("%d ", dpl[i]);
// printf("dpl\n");
// printf("dpr\n");
// for(int i = 0 ; i < n ; i++)
// printf("%d ", dpr[i]);
// printf("dpr\n");
int ans = 0;
for(int i = 0 ; i < n ; i++)
ans = max(ans, min(dpl[i], dpr[i]) * 2 - 1);
printf("%d\n", ans);
}
return 0;
}
UVA 10051
搞清楚状态转移就行,比如用这个重量的front的dp值去更新重量更大的方块的back的dp值,具体用异或处理一下就可以。数组开小WA了几次。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
const int MAXN = 3000 + 5;
int dp[MAXN * 6];
int path[MAXN * 6];
int d[MAXN][6];
vector<int>lin[105];
int x[MAXN], y[MAXN];
int main()
{
int n;
int cas = 0;
while(scanf("%d", &n) != EOF && n){
for(int i = 0 ; i <= 100 ; i++)
lin[i].clear();
for(int i = 0 ; i < n ; i++){
for(int j = 0 ; j < 6 ; j++){
scanf("%d", &d[i][j]), dp[i * 6 + j] = 1;
path[i * 6 + j] = i * 6 + j;
lin[d[i][j]].push_back(i * 6 + j);
}
}
int ans = 0;
int mark = 0;
for(int i = 0 ; i < n ; i++){
for(int j = 0 ; j < 6 ; j++){
int now = (i * 6 + (j ^ 1));
int k = lower_bound(lin[d[i][j]].begin(), lin[d[i][j]].end(), now) - lin[d[i][j]].begin();
for(; k < (int)lin[d[i][j]].size() ; k++){
int ne = lin[d[i][j]][k];
if(ne / 6 <= now / 6) continue;
// if(i == 0 && j == 0){
// printf("now = %d, ne = %d\n", now, ne);
// }
if(dp[ne] < dp[now] + 1){
dp[ne] = dp[now] + 1;
path[ne] = now;
}
}
if(dp[now] > ans){
ans = dp[now], mark = now;
}
}
}
int cnt = 0;
while(1){
x[cnt] = mark / 6, y[cnt++] = mark % 6;
if(path[mark] == mark) break;
mark = path[mark];
}
if(cas != 0) printf("\n");
printf("Case #%d\n", ++cas);
printf("%d\n", ans);
for(int i = cnt - 1 ; i >= 0 ; i--){
printf("%d ", x[i] + 1);
if(y[i] == 0) printf("front\n");
else if(y[i] == 1) printf("back\n");
else if(y[i] == 2) printf("left\n");
else if(y[i] == 3) printf("right\n");
else if(y[i] == 4) printf("top\n");
else if(y[i] == 5) printf("bottom\n");
}
// printf("\n");
}
return 0;
}
/* 3 1 2 2 2 1 2 3 3 3 3 3 3 3 2 1 1 1 1 10 1 5 10 3 6 5 2 6 7 3 6 9 5 7 3 2 1 9 1 3 3 5 8 10 6 6 2 2 4 4 1 2 3 4 5 6 10 9 8 7 6 5 6 1 2 3 4 7 1 2 3 3 2 1 3 2 1 1 2 3 0 */
UVA 10651
用状压DP的方式模拟+记忆化就可以。
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
const int MAXN = 13;
int dp[1<<MAXN];
char str[20];
int solve(int s)
{
if(dp[s] != -1) return dp[s];
int val = 0;
for(int i = 0 ; i < 12 ; i++) if((1 << i) & s) val++;
dp[s] = val;
for(int i = 0 ; i < 12 ; i++){
if((s & (1 << i))){
if(i > 1 && (s & (1 << (i - 1))) && !(s & (1 << (i - 2)))){
int ts = (s ^ (1 << (i - 2)) ^ (1 << (i - 1)) ^ (1 << (i)));
dp[s] = min(dp[s], solve(ts));
}
if(i < 10 && (s & (1 << (i + 1))) && !(s & (1 << (i + 2)))){
int ts = (s ^ (1 << (i + 2)) ^ (1 << (i + 1)) ^ (1 << (i)));
dp[s] = min(dp[s], solve(ts));
}
}
}
return dp[s];
}
int main()
{
memset(dp, -1, sizeof(dp));
int t;
scanf("%d", &t);
while(t--){
scanf("%s", str);
int ts = 0;
for(int i = 11 ; i >= 0 ; i--){
if(str[i] == 'o') ts += (1 << i);
}
printf("%d\n", solve(ts));
}
return 0;
}