Uva1025
题意:这个城市有n个车站,每隔一段时间会从所有车站的一遍发出一列列车,有一个要从第一个车站出发,目的是在时间T会见车站n 的一个人,需要换乘,问在不同车站换成所需要等待的最短时间是多少
解法:d(i,j)表示在时刻i,你在j车站所需要等待的最长时间,有三种决策,1. 等一分钟、2. 搭乘往右开的车、3. 搭乘往左开的车
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int inf = 0x3fffffff;
int dp[200 + 5][50 + 5];//在时间t,车站i应该等待的最长时间
int has_train[205][55][2], t[50 + 5];//has_train表示在时间t车站i是否有列车
int d[50 + 5], e[50 + 5];//往右开的第一班车,往左开的第一班车
int main() {
int n, T, tmp = 1;
while (scanf("%d%d", &n, &T)&&n) {
memset(dp, 0, sizeof(dp)); memset(has_train, 0, sizeof(has_train));
memset(t, 0, sizeof(t)); memset(d, 0, sizeof(d)); memset(e, 0, sizeof(e));
for (int i = 1; i <= n - 1; i++) scanf("%d", &t[i]);
int m1; scanf("%d", &m1);
for (int i = 1; i <= m1; i++) {
scanf("%d", &d[i]);
int tmp = d[i];
for (int j = 1; j <= n - 1; j++) {
if (tmp <= T) has_train[tmp][j][0] = 1;
tmp += t[j];
}
}
int m2; scanf("%d", &m2);
for (int i = 1; i <= m2; i++) {
scanf("%d", &e[i]);
int tmp = e[i];
for (int j = n - 1; j >= 1; j--) {
if (tmp <= T) has_train[tmp][j + 1][1] = 1;
tmp += t[j];
}
}
for (int i = 1; i <= n - 1; i++)dp[T][i] = inf;
dp[T][n] = 0;
for (int i = T - 1; i >= 0; i--)
for (int j = 1; j <= n; j++) {
dp[i][j] = dp[i + 1][j] + 1;//时间先前流动一秒
if (j < n&&has_train[i][j][0] && i + t[j] <= T)
dp[i][j] = min(dp[i][j], dp[i + t[j]][j + 1]);
if (j > 1 && has_train[i][j][1] && i + t[j - 1] <= T)
dp[i][j] = min(dp[i][j], dp[i + t[j - 1]][j - 1]);
}
printf("Case Number %d: ", tmp++);
if (dp[0][1] >= inf) printf("impossible\n");
else printf("%d\n", dp[0][1]);
}
return 0;
}
Uva437
题意:有n种正方体,每种都有无穷多个。要求选一些立方体摞成一根尽量高的柱子,使得每个立方体的地面长宽严格小于它下方立方体的地面长宽
解法:见代码
/*
思路:
因为a和b的值会很大,所以我们不能直接用d(a,b)来表示状态值;
所以我们(idx,h)来表示这个状态,h存放的是立方体的的三条高,知道其中的一条我们就能知道对应的面
idx表示立方体的编号
ans就是我们要求的那个结果,即最大高度,状态转移就是简单的拿或者不拿那个立方体
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 30 + 5;
int n, blocks[maxn][3], d[maxn][3];
void get_dimensions(int* v, int b, int dim) {
int idx = 0;
for (int i = 0; i < 3; i++) {
if (i != dim) v[idx++] = blocks[b][i];
}
}
int dp(int i,int j) {
int&ans = d[i][j];
if (ans > 0)return ans;
ans = 0;
int v[2], v2[2];
get_dimensions(v, i, j);
for(int a=0;a<n;a++)
for(int b=0;b<3;b++){//a和b是底面的边长
get_dimensions(v2, a, b);
if (v2[0] < v[0] && v2[1] < v[1]) ans = max(ans, dp(a, b));
}
ans += blocks[i][j];
return ans;
}
int main() {
int kase = 0;
while (scanf("%d", &n) && n) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < 3; j++)
scanf("%d", &blocks[i][j]);
sort(blocks[i], blocks[i] + 3);
}
memset(d, 0, sizeof(d));
int ans = -1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < 3; j++) {
ans = max(ans, dp(i, j));
}
}
printf("Case %d: maximum height = %d\n", ++kase, ans);
}
return 0;
}
Uva116
题意:有一个m行n列的整数矩阵,从第一列任何一个位置出发每次往↗↘或者→走一格,最终到达最后一列,要求经过的整数之和最小。整个矩形是环形的,即第一行的上一行是最后一行,最后一行的下一行是第一行。输出路径上每列的行号。多解释输出字典序最小的
解法:见代码
/*
多阶段决策问题,重点是行和列的处理,注意看一下
还有一个重点是结点路径的保存
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int inf = 0x3fffffff;
int a[1010][1010], d[1010][1010], nex[1010][1010];
int main() {
int n, m;
while (scanf("%d%d", &m, &n) ==2 && m ) {//m行,n列
memset(a, 0, sizeof(a)); memset(d, 0, sizeof(d));
memset(nex, 0, sizeof(nex));
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++) {
scanf("%d", &a[i][j]);
}
int ans = inf, first = 0;
for (int j = n - 1; j >= 0; j--) {//从每一行开始
for (int i = 0; i < m; i++) {//从每一列开始
if (j == n - 1)d[i][j] = a[i][j];//设置初始值
else {
int row[3] = { i,i - 1,i + 1 };//三个方向
if (i == 0)row[1] = m - 1;//上一行是下边界
if (i == m - 1)row[2] = 0;//下一行是上边界
sort(row, row + 3);
d[i][j] = inf;
for (int k = 0; k < 3; k++) {
int v = d[row[k]][j + 1] + a[i][j];
//↓↓↓nex[i][j]表示从(i,j)出发的下一个坐标
if (v < d[i][j]) { d[i][j] = v; nex[i][j] = row[k]; }
}
}
if (j == 0 && d[i][j] < ans) { ans = d[i][j]; first = i; }//更新出发点
}
}
printf("%d", first + 1);
for (int i = nex[first][0], j = 1; j < n; i = nex[i][j], j++) {
printf(" %d", i + 1);
}printf("\n%d\n", ans);
}
return 0;
}
Uva12563
题意:有n首歌,剩余时间为UP,每首歌都有一个时间长度,要求你在剩余时间内尽可能多的唱歌,同时要求唱的时间尽可能的长
解法:01背包,双状态
/*
背包问题的变式,这道题的解法涉及到了一个dp中核心的解法:状态
注意枚举上界UP要-1
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn = 180 * 50 + 5;
struct node {
int num, len;
bool operator < (const node& rhs) const {
return num < rhs.num || (num == rhs.num&&len < rhs.len);
}
};
node dp[maxn];
ll t[maxn];
int main() {
int T; scanf("%d", &T);
int kase = 1;
while (T--) {
ll n, UP; scanf("%lld%lld", &n, &UP);
memset(t, 0, sizeof(t)); memset(dp, 0, sizeof(dp));
ll sum = 0;
for (int i = 1; i <= n; i++) scanf("%lld", &t[i]), sum += t[i];
UP -= 1;
for (int i = 1; i <= n; i++) {
for (int j = UP; j >= 0; j--) {
if (j < t[i])break;//剩余时间都不够t[i]了,没必要再选择了
node tmp = { dp[j - t[i]].num + 1,dp[j - t[i]].len + t[i] };
dp[j] = max(dp[j], tmp);//选取更优状态
}
}
printf("Case %d: %d %d\n", kase++, dp[UP].num + 1, dp[UP].len + 678);
}
return 0;
}
Uva1625
题意:有两个字符串,每次可以选两个首位的字符将他们加入到新串的尾部,对于每个颜色来说(每个颜色就是每个字符),其跨度L(c)等于最大位置和最小位置之差。找到一种合并方式,使得所有的L(c)总和最小
解法:见代码
*
非常好的思路题,多多品味
*/
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 5e3 + 5;
const int inf = 0x3fffffff;
int dp[maxn][maxn], cnt[maxn][maxn];
int bf[100], bs[100], ef[100], es[100];
char f[maxn], s[maxn];
//dp[i][j]为从第一个颜色序列中拿走前i个颜色,从第二个颜色中拿走前j个颜色时合并产生的序列的最小lc之和
//cnt[i][j]为分别取走i个颜色和j个颜色时还有多少种颜色已经出现但尚未结束
//bf[c]为f串中c字母出现的第一个位置,es[c]为f串中c字母出现的最后一个位置
//ef同上,只不过对应s串中的情况
int main() {
int T; scanf("%d", &T);
while (T--) {
scanf("%s%s", f + 1, s + 1);
int lenf = strlen(f+1), lens = strlen(s+1);
for (char c = 'A'; c <= 'Z'; c++) {
bf[c] = bs[c] = inf;
ef[c] = es[c] = 0;
}
//处理开始值和终点值,注意处理开始值的时候采用最值的方法可以避免定义first变量
for (int i = 1; i <= lenf; i++) {
char c = f[i];
bf[c] = min(i, bf[c]);
ef[c] = i;
}
for (int i = 1; i <= lens; i++) {
char c = s[i];
bs[c] = min(i, bs[c]);
es[c] = i;
}
//处理cnt数组
for (int i = 0; i <= lenf; i++)
for (int j = 0; j <= lens; j++) {
if (i) {
cnt[i][j] = cnt[i - 1][j];//取走f数组中的一个颜色
char c = f[i];
if (bf[c] == i&&bs[c] > j)cnt[i][j]++;//f中已经出现但是s中没有出现
if (ef[c] == i&&es[c] <= j)cnt[i][j]--;//f中已经结束或者s中已经结束
//上面这个还值得推敲
}
if (j) {
cnt[i][j] = cnt[i][j - 1];
char c = s[j];
if (bs[c] == j&&bf[c] > i)cnt[i][j]++;
if (es[c] == j&&ef[c] <= i)cnt[i][j]--;
}
}
for (int i = 0; i <= lenf; i++)
for (int j = 0; j <= lens; j++) {
if (!i && !j)continue;
dp[i][j] = inf;
//这点非常重要,+cnt[i][j]其实将所有的字母的情况都考虑进去了
if (i)dp[i][j] = min(dp[i][j], dp[i - 1][j] + cnt[i - 1][j]);
if (j)dp[i][j] = min(dp[i][j], dp[i][j - 1] + cnt[i][j - 1]);
}
printf("%d\n", dp[lenf][lens]);
}
return 0;
}