题目链接:1025 - A Spy in the Metro
参考博文:UVA_1025_A_Spy_in_the_Metro_(动态规划)
代码:
#include
#include
#include
using namespace std;
const int INF = 1<<29;
int dp[500][100], t[500];
int has_train[500][100][3];
int n, M1, m1, M2, m2, T;
void Init()
{
memset(has_train, 0, sizeof(has_train));
}
int main()
{
int kase = 1;
while(scanf("%d", &n)!=EOF && n)
{
Init();
scanf("%d", &T);
for(int i=1; i<n; i++)
scanf("%d", &t[i]);
scanf("%d", &M1);
//初始化
for(int i=0; i<M1; i++)
{
scanf("%d", &m1);
for(int j=1; j<=n; j++)
{
has_train[m1][j][0] = 1;
m1 += t[j];
}
}
scanf("%d", &M2);
for(int i=0; i<M2; i++)
{
scanf("%d", &m2);
for(int j=n; j>=1; j--)
{
has_train[m2][j][1] = 1;
m2 += t[j-1];
}
}
for(int i=1; i<n; 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: ", kase++);
if(dp[0][1]>=INF) printf("impossible\n");
else printf("%d\n", dp[0][1]);
}
return 0;
}
题目链接:437 - The Tower of Babylon
我的AC代码,有点冗长,性能不好
代码:
#include
#include
#include
#include
using namespace std;
int n;
int dp[50][4], a[50][4];
void Init(int n)
{
for(int i=0; i<=n; i++)
{
for(int j=0; j<3; j++)
dp[i][j] = 0;
}
}
//检查a[k][t]是否可以在a[i][j]上
bool check(int i, int j, int k, int t)
{
int m1[3], m2[3], cnt1 = 0, cnt2 = 0;
for(int p=0; p<3; p++)
{
if(p!=j)
m1[cnt1++] = a[i][p];
if(p!=t)
m2[cnt2++] = a[k][p];
}
for(int p=0; p<2; p++)
{
if(m1[p]<=m2[p]) return 0;
}
return 1;
}
//DAG记忆化搜索
int solve(int i, int j)
{
int &ans = dp[i][j];
if(ans>0) return ans;
ans = a[i][j];
int top = 0;
for(int k=0; k<n; k++)
{
for(int t=0; t<3; t++)
{
if(check(i, j, k, t))
top = max(top, solve(k, t));
}
}
ans = top+ans;
return ans;
}
int main()
{
int kase = 1;
while(scanf("%d", &n)!=EOF && n)
{
for(int i=0; i<n; i++)
{
for(int j=0; j<3; j++)
{
scanf("%d", &a[i][j]);
}
sort(a[i], a[i]+3);
}
Init(n);
int Max = 0;
//任何一个状态均可以作为起始点
//寻找路径最长的状态
for(int i=0; i<n; i++)
{
for(int j=0; j<3; j++)
Max = max(Max, solve(i, j));
}
printf("Case %d: maximum height = %d\n", kase++, Max);
}
return 0;
}
网上代码:
#include
#include
#include
using namespace std;
const int maxn=30+2;
struct node{
int x,y,z;
node(int x=0,int y=0,int z=0):x(x),y(y),z(z){}
bool operator<(const node& n)const {//用于判断二者是否可以相连
return x<n.x&&y<n.y || x<n.y&&y<n.x;
}
}nt[maxn*3];
int g[maxn*3][maxn*3];
int d[maxn*3];//表示以第i个状态作为底的高度
int n;
int dp(int i,int h){
int& ans=d[i];
if(ans>0)return ans;
ans=h;
for(int j=0;j<n*3;j++)
if(g[i][j])ans=max(ans,dp(j,nt[j].z)+h);//当第j个状态可以放在第i个状态上方时
return ans;
}
int main(){
int count1=0;
while(scanf("%d",&n)==1 && n){
int c=0;
for(int i=0;i<n;i++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
nt[c++]=node(x,y,z); //第三位表示高度
nt[c++]=node(x,z,y);
nt[c++]=node(y,z,x);
}
memset(d,0,sizeof(d));
memset(g,0,sizeof(g));
for(int i=0;i<n*3;i++)
for(int j=0;j<n*3;j++)
if(nt[i]<nt[j])g[j][i]=1;//表示第i个状态可以放在第j个状态上方
int ans=-1e5;
for(int i=0;i<n*3;i++)
ans=max(ans,dp(i,nt[i].z));
printf("Case %d: maximum height = %d\n",++count1,ans);
}
return 0;
}
题目链接:1347 - Tour
参考博文:UVA 1347 Tour
代码:
#include
#include
#include
#include
using namespace std;
const int MAX = 1001;
int n;
struct Node
{
int x, y;
};
Node point[MAX];
double dist[MAX][MAX];
double dp[MAX][MAX];
double DP(int i, int j)
{
double &ans = dp[i][j];
if(ans>0) return ans;//表明该值已经计算出
ans = min(dist[i][i+1]+DP(i+1, j), dist[j][i+1]+DP(i+1, i));
return ans;
}
double compute(int i, int j)
{
return sqrt(1.0*(point[i].x - point[j].x)*(point[i].x - point[j].x) + 1.0*(point[i].y - point[j].y)*(point[i].y - point[j].y));
}
int main()
{
while(scanf("%d", &n)!=EOF && n)
{
for(int i=1; i<=n; i++)
scanf("%d%d", &point[i].x, &point[i].y);
//初始化
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
dp[i][j] = -1.0;
dist[i][j] = dist[j][i] = compute(i, j);
}
}
for(int i=1; i<=n; i++)
dp[n-1][i] = dist[n-1][n]+dist[i][n];
printf("%.2f\n", DP(1, 1));
}
return 0;
}
题目链接:116 - Unidirectional TSP
代码:
#include
#include
#include
#include
using namespace std;
const int INF = 1<<29;
int G[15][105], dp[15][105];
int m, n;
int main()
{
int next[15][105];
while(scanf("%d%d", &m, &n)!=EOF)
{
//输入
for(int i=0; i<m; i++)
{
for(int j=0; j<n; j++)
{
scanf("%d", &G[i][j]);
}
}
int ans = INF, first = 0;//ans是最短路径值,first是最短路的起始行
for(int j=n-1; j>=0; j--)
{
for(int i=0; i<m; i++)
{
if(j==n-1) dp[i][j] = G[i][j];//边界条件
else
{
int rows[3] = {i, i-1, i+1};
if(i==0) rows[1] = m-1;
if(i==m-1) rows[2] = 0;
sort(rows, rows+3);//按序,方便找到字典序最小
dp[i][j] = INF;
for(int k=0; k<3; k++)
{
int v = dp[rows[k]][j+1]+G[i][j];
if(v<dp[i][j])
{
dp[i][j] = v;
next[i][j] = rows[k];//(i,j)点的点的行
}
}
}
if(j==0 && dp[i][j]<ans)//找到最短路的起始点
ans = dp[i][j], first = i;
}
}
printf("%d", first+1);
for(int i=next[first][0], j=1; j<n; i=next[i][j], j++)
printf(" %d", i+1);
printf("\n%d\n", ans);
}
return 0;
}
题目链接:12563 - Jin Ge Jin Qu hao
代码:
#include
using namespace std;
const int MAX = 10000;
int n;
int m;
int num[55][MAX], dp[55][MAX], t[55];
int main()
{
int T, kase = 1;
scanf("%d", &T);
while(T--)
{
memset(num, 0, sizeof(num));
memset(dp, 0, sizeof(dp));
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++)
scanf("%d", &t[i]);
for(int i=1; i<=n; i++)
{
for(int j=0; j<=m; j++)
{
if(j>t[i])//注意不能带等于号,因为结束时不能开始新歌
{
dp[i][j] = dp[i-1][j]; num[i][j] = num[i-1][j];
//表示当选择该歌歌的数目增加||(歌的数目不增加&&增加该歌歌的时长增加)
if(num[i-1][j-t[i]]+1 > num[i][j] || (num[i-1][j-t[i]]+1 == num[i][j] && dp[i-1][j-t[i]]+t[i] > dp[i][j]))
{
num[i][j] = num[i-1][j-t[i]]+1;
dp[i][j] = dp[i-1][j-t[i]]+t[i];
}
}
else//
{
num[i][j] = num[i-1][j];
dp[i][j] = dp[i-1][j];
}
}
}
printf("Case %d: %d %d\n", kase++, num[n][m]+1, dp[n][m]+678);
}
return 0;
}
题目链接:11400 - Lighting System Design
代码:
#include
#include
#include
#include
#include
using namespace std;
const int INF = 1<<29;
const int MAX = 1005;
struct Node
{
int v, l, k, c;
bool operator < (const Node &A) const
{
return v<A.v;
}
};
Node A[MAX];
int s[MAX], dp[MAX];
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif
int n;
while(scanf("%d", &n)!=EOF && n)
{
for(int i=1; i<=n; i++)
{
scanf("%d%d%d%d", &A[i].v, &A[i].k, &A[i].c, &A[i].l);
}
sort(A+1, A+n+1);
s[0] = 0;//注意s数组一定在排序后再赋值
for(int i=1; i<=n; i++)
s[i] = s[i-1]+A[i].l;
fill(dp, dp+n+1, INF);
dp[0] = 0;
for(int i=1; i<=n; i++)
{
//两种状态转化的书写方式
for(int j=i-1; j>=0; j--)
{
dp[i] = min(dp[i], dp[j]+(s[i]-s[j])*A[i].c+A[i].k);
}
/* dp[i] = s[i]*A[i].c+A[i].k; for(int j=1; j<=i; j++) { dp[i] = min(dp[i], dp[j]+(s[i]-s[j])*A[i].c+A[i].k); }*/
}
printf("%d\n", dp[n]);
}
return 0;
}
题目链接:1625 - Color Length
参考博文:Color Length(UVA-1625)(DP LCS变形)
博文
#include
#include
#include
using namespace std;
const int maxn = 5000+5;
const int INF = 1000000000;
char p[maxn],q[maxn];
int sp[26],sq[26],ep[26],eq[26];
int d[maxn],c[maxn];//其他人博客貌似都是两层,但其实一层就够了。
//d[]表示在第二个串放入j个时,位置差之和,c[]表示在第二个串放入j个时,已经开始但未结束的字符的个数
int main()
{
int T;
cin>>T;
while(T--)
{
scanf("%s%s",p+1,q+1);//下标从1开始
int n = strlen(p+1),m = strlen(q+1);
for(int i=1;i<=n;i++) p[i]-='A';
for(int j=1;j<=m;j++) q[j]-='A';
for(int i=0;i<26;i++)
{
sp[i] = sq[i] = INF;
ep[i] = eq[i] = 0;
}
//get到这个预处理,以前还没遇到过,标记每一个字符的最开始下标和最末尾下标
for(int i=1;i<=n;i++)
{
sp[p[i]] = min(sp[p[i]],i);
ep[p[i]] = i;
}
for(int i=1;i<=m;i++)
{
sq[q[i]] = min(sq[q[i]],i);
eq[q[i]] = i;
}
memset(c,0,sizeof c);
memset(d,0,sizeof d);
for(int i=0;i<=n;i++)
{
for(int j = 0;j<=m;j++)//表示在第一个串已经放入i个时,开始放第二个串
{
if(!j&&!i) continue;//两个都是0,就直接继续。
int v1=INF,v2 = INF;//对于这种特殊情况,只能先把两个值先寄存在v1,v2,并且赋值为INF。
if(i) v1=d[j]+c[j];
if(j) v2=d[j-1]+c[j-1];
d[j] = min(v1,v2);
if(i)//当i不等于0,判断是否有新字符开始或旧字符结束
{
c[j] = c[j];
if(sp[p[i]]==i&&sq[p[i]]>j)c[j]++;//当该字符已经放入,
if(ep[p[i]]==i&&eq[p[i]]<=j)c[j]--;
}
else if(j)//当i等于0,判断是否有新字符开始或旧字符结束
{
c[j] = c[j-1];
if(sq[q[j]] == j && sp[q[j]]>i)c[j]++;
if(eq[q[j]] == j && ep[q[j]]<=i)c[j]--;
}
}
}
printf("%d\n",d[m]);
}
return 0;
}
题目链接:11584 - Partitioning by Palindromes
代码:
#include
using namespace std;
const int maxn = 1000 + 10;
const int INF = 0x3f3f3f3f;
char str[maxn];
int is_palindromes[maxn][maxn];
int dp[maxn];
//递归判断是否是回文串,值得学习
int Is_palindromes(int j, int i) {
if (j >= i) return 1;
if (is_palindromes[j][i] != -1) return is_palindromes[j][i];
if (str[i] == str[j]) {
return is_palindromes[j][i] = Is_palindromes(j + 1, i - 1);
}
else return is_palindromes[j][i] = 0;
}
int main()
{
//freopen("input.txt", "r", stdin);
int iCase;
scanf("%d", &iCase);
while (iCase--) {
scanf("%s", str + 1);//下标从1开始
memset(is_palindromes, -1, sizeof(is_palindromes));
dp[0] = 0;
int len = strlen(str + 1);
for (int i = 1; i <= len; i++) {//遍历下标,i为终止下标
dp[i] = i;
for (int j = 0; j < i; j++) {//查看加上该串是否构成回文串,j为起始坐标
if (Is_palindromes(j + 1, i)) dp[i] = min(dp[i], dp[j] + 1);
}
}
printf("%d\n", dp[len]);
}
return 0;
}
题目链接:10003 - Cutting Sticks
注意不能采用哈夫曼法:选择小木块合并的规则并不是找任意两个最小木块,而是有“相邻”这一条件限制的!
递归法代码:
#include
#include
#include
#include
using namespace std;
const int maxn = 55;
int l, n, dp[maxn][maxn], a[maxn],cost[maxn][maxn];
int main()
{
while(~scanf("%d", &l), l)
{
memset(dp, 0x3f3f3f3f, sizeof(dp));
memset(cost, 0, sizeof(cost));
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
}
dp[n][n+1] = 0;
a[n+1] = l;
a[0] = 0;
sort(a+1, a+n+1);
for(int i = 0; i <= n; i++)//一个木棍不需要分割
dp[i][i+1] = 0;
for(int len = 2; len <= n+1; len++)//木棍个数
for(int i = 0; i + len <= n+1; i++)//起始切割点下标
{
int j = i + len;//终止切割点下标
for(int k = i+1; k < j; k++)//分割点
dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j]+a[j]-a[i]);
}
printf("The minimum cutting is %d.\n", dp[0][n+1]);
}
return 0;
}
记忆化搜索代码:
#include
#include
#include
#include
#include
using namespace std;
const int INF = 1<<29;
//dp[i][j]表示切割第i个切割点和第j个切割点之间的木棍的最小代价
int dp[55][55], n, l, a[55], x;
int DP(int i, int j)
{
int &ans = dp[i][j];
if(ans>=0) return ans;
ans = INF;
for(int k=i+1; k<j; k++)
{
ans = min(ans, DP(i, k)+DP(k, j)+a[j]-a[i]);//注意a[j]-a[i]要写在括号内
}
return ans;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif
while(scanf("%d", &l)!=EOF && l)
{
scanf("%d", &n);
a[0] = 0;
memset(dp, -1, sizeof(dp));
for(int i=1; i<=n; i++)
{
scanf("%d", &a[i]);
dp[i-1][i] = 0;
}
a[n+1] = l;
dp[n][n+1] = 0;
printf("The minimum cutting is %d.\n", DP(0, n+1));
}
return 0;
}
代码:
#include
#include
#include
using namespace std;
char s[101];
int dp[101][101];//dp[i][j]表示s[i..j]中至少需要添加几个括号
int n;
int match(char a, char b)
{
return (a=='('&&b==')')||(a=='['&&b==']');
}
void DP()
{
for(int i=0; i<n; i++)
{
dp[i][i] = 1;
dp[i+1][i] = 0;//对应空串
}
for(int i=n-2; i>=0; i--)//起始坐标
{
for(int j=i+1; j<n; j++)//终止坐标
{
dp[i][j] = n;
if(match(s[i], s[j]))
dp[i][j] = min(dp[i][j], dp[i+1][j-1]);
for(int k=i; k<j; k++)
dp[i][j] = min(dp[i][j], dp[i][k]+dp[k+1][j]);
}
}
}
//递归打印
void print(int i,int j)
{
if(i>j) return;
if(i==j)//当只有一个字符单括号
{
if(s[i]=='('||s[i]==')')
printf("()");
else
printf("[]");
return;
}
int ans=dp[i][j];
if(match(s[i],s[j])&&ans==dp[i+1][j-1])//当有双括号
{
printf("%c",s[i]);print(i+1,j-1);printf("%c",s[j]);
return;
}
for(int k=i;k<j;k++)
if(ans==dp[i][k]+dp[k+1][j])
{
print(i,k);print(k+1,j);
return;
}
}
int main()
{
int T;
scanf("%d", &T);
getchar();
while(T--)
{
fgets(s, 101, stdin);
n = strlen(s)-1;
//cout << n << endl;
memset(dp, -1, sizeof(dp));
DP();
print(0, n-1);
printf("\n");
if(T) printf("\n");
//fgets(s, 101, stdin);
}
return 0;
}
题目链接:10285 - Longest Run on a Snowboard
代码:
#include
#include
#include
using namespace std;
char name[1000];
int G[105][105], dp[105][105], n, m;
int dx[5] = {0, 0, 1, -1};
int dy[5] = {1, -1, 0, 0};
int DP(int i, int j)
{
int &ans = dp[i][j];
if(ans>0) return ans;
ans = 1;
for(int k=0; k<4; k++)
{
int x = dx[k]+i, y = dy[k]+j;
if(x<1 || x>n || y<1 || y>m) continue;
if(G[x][y]<G[i][j])
ans = max(ans, DP(x, y)+1);
}
return ans;
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%s%d%d", name, &n, &m);
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
scanf("%d", &G[i][j]);
}
}
memset(dp, 0, sizeof(dp));
int Max = 0;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
Max = max(Max, DP(i, j));
}
}
printf("%s: %d\n", name, Max);
}
return 0;
}
题目链接:10118 - Free Candies
代码:
#include
#include
#include
#include
using namespace std;
int num[45][5], n;
int vis[25], top[5];//vis记录某个颜色是否在篮子里,top记录每一堆已经拿了几个
int dp[45][45][45][45];
int DP(int k)
{
int &ans = dp[top[0]][top[1]][top[2]][top[3]];
if(ans>0) return ans;
ans = 0;//一定在判断k之前赋值为0
if(k>=5) return ans;//超出时可获得0个
for(int i=0; i<4; i++)
{
if(top[i]==n) continue;//该堆已经拿完
if(vis[num[top[i]][i]])//该颜色重复
{
vis[num[top[i]][i]] = 0;
top[i]++;
ans = max(ans, DP(k-1)+1);
top[i]--;
vis[num[top[i]][i]] = 1;
}
else
{
vis[num[top[i]][i]] = 1;
top[i]++;
ans = max(ans, DP(k+1));
top[i]--;
vis[num[top[i]][i]] = 0;
}
}
return ans;
}
int main()
{
while(scanf("%d", &n)!=EOF && n)
{
for(int i=0; i<n; i++)
{
for(int j=0; j<4; j++)
{
scanf("%d", &num[i][j]);
}
}
memset(vis, 0, sizeof(vis));
memset(dp, -1, sizeof(dp));
memset(top, 0, sizeof(top));
printf("%d\n", DP(0));
}
return 0;
}
题目链接:1629 - Cake slicing
代码:
#include
#include
#include
using namespace std;
const int INF = 1<<29;
int n, m, k;
int dp[21][21][21][21];
int num[21][21][21][21];//存储该矩形内有多少个草莓
struct Node
{
int x, y;
};
Node c[500];
int check(int x1, int y1, int x2, int y2)
{
if(num[x1][y1][x2][y2]) return num[x1][y1][x2][y2];
int cnt = 0;
for(int i=0; i<k; i++)
{
if(c[i].x>=x1 && c[i].x<=x2 && c[i].y>=y1 && c[i].y<=y2) cnt++;
}
return num[x1][y1][x2][y2]=cnt;
}
int DP(int x1, int y1, int x2, int y2)
{
if(check(x1, y1, x2, y2)<2) return 0;
int &ans = dp[x1][y1][x2][y2];
if(ans!=-1) return ans;
ans = INF;
for(int i=x1; i<x2; i++)
{
if(check(x1, y1, i, y2)<1 || check(i+1, y1, x2, y2)<1) continue;
ans = min(ans, DP(x1, y1, i, y2)+DP(i+1, y1, x2, y2)+y2-y1+1);
}
for(int j=y1; j<y2; j++)
{
if(check(x1, y1, x2, j)<1 || check(x1, j+1, x2, y2)<1) continue;
ans = min(ans, DP(x1, y1, x2, j)+DP(x1, j+1, x2, y2)+x2-x1+1);
}
return ans;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
#endif
int kase = 1;
while(cin >> n >> m >> k)
{
for(int i=0; i<k; i++)
{
cin >> c[i].x >> c[i].y;
}
memset(dp, -1, sizeof(dp));
memset(num, 0, sizeof(num));
printf("Case %d: %d\n", kase++, DP(1, 1, n, m));
}
return 0;
}