UVA 几道dp题总结

先给一个Virtual Judge 上挂的地址 dp

A - A Spy in the Metro

题意

起点站和终点站之间有若干个站点,同时,在起点站和终点站会在若干的固定时间始出列车。某人将从起点站出发,在t时到终点站见人,但是希望在站点等尽量少的时间。求出最少的等待时间。

分析

dp。。
“时间 是 一个天然的序”,所以,影响决策的只有时间和当前的站点。
dp[i][j] 表示 在 第i时间 在 j 站点的最小等待时间。所以每一个都面临最多三种的决策:

  • 在原地等待一分钟
  • 选择向左开的列车
  • 选择向右开的列车

    三种决策中选择最小等待时间的。
    考虑边界条件:dp[T][n]= 0, 其他时间的dp[T][i] 预先设成正无穷,在dp过程中无法选择。

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int maxn = 50 + 5;
const int maxt = 200 + 5;
const int INF = 1000000000;

// has_train[t][i][0]表示时刻t,在车站i是否有往右开的火车
int t[maxn], has_train[maxt][maxn][2];
int dp[maxt][maxn];

int main()
{
    int kase = 0, n, T;
    while(cin >> n >> T && n)
    {

        int M1, M2, d;
        for(int i = 1; i <= n-1; i++) cin >> t[i];

        // 预处理,计算has_train数组
        memset(has_train, 0, sizeof(has_train));
        cin >> M1;
        while(M1--)
        {
            cin >> d;
            for(int j = 1; j <= n-1; j++)
            {
                if(d <= T) has_train[d][j][0] = 1;
                d += t[j];
            }
        }
        cin >> M2;
        while(M2--)
        {
            cin >> d;
            for(int j = n-1; j >= 1; j--)
            {
                if(d <= T) has_train[d][j+1][1] = 1;
                d += t[j];
            }
        }

        // DP主过程
        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]); // 左
            }

        // 输出
        //cout << dp[0][1] << endl;
        cout << "Case Number " << ++kase << ": ";
        if(dp[0][1] >= INF) cout << "impossible\n";
        else cout << dp[0][1] << "\n";
    }
    return 0;
}

后续的题目待续。。

C-Tour

这一道问题有一个十分高大上的问题,叫做 双调旅行商问题。

题意

某人从最左的一点出发,按照严格从左到右的顺序移动到最右一点。然后从最右点返回,按照严格从右到左的顺序回到最左点,并且每一个点都只能访问一次。求最小的距离。

抽象

给你 n 个二维坐标上的点,你需要经过所有的点然后走回原地,求最短路径。

思路

首先,想成是两个人从最左点出发,一同走到最右点,两人中间的访问地点不能相同。可以用dp(i,j),表示1~max{i,j}的点全部走完了,第一个人走到i点,第二个人走到j点,还需要多长的距离。

状态间的转移方式:
为防止dp(i,j) = dp(j,i),对解的产生冲突。
现在同时加上 强制条件:i > j。
原先 可以写成:dp(i,j)= dp(i+1,j)+ dp(i,j+1)。(这是向i+1点转移的方式)
但是 由 j 点转移到 i+1点是不符合条件的,所以先将i,j 对调,再让j点移动到i+1点。于是可以变成 dp(i+1, i);
所以, dp(i,j) = dp(i+1, j) + dp(i+1, i);

边界条件:dp(n-1, j) = dis(n, n-1) + dis(n,j)

代码

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <string>
#include <bitset>
#include <cstdio>
#include <limits>
#include <vector>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <fstream>
#include <numeric>
#include <sstream>
#include <iostream>
#include <algorithm>
#define MEM(a,x) memset(a,x,sizeof(a))
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int maxn = 1010;
double dp[maxn][maxn];
double dis[maxn][maxn];
struct Point
{
    int x, y;
};
Point p[maxn];
double dist(Point a, Point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    while(cin >> n)
    {
        for(int i = 1; i <= n; i++)
        {
            cin >> p[i].x >> p[i].y;
        }
        for(int i = 1; i <= n; i++)
        {
            for(int j = i+1; j <= n; j++)
            {
                dis[i][j] = dis[j][i] = dist(p[i], p[j]);
            }
        }
        for(int j = 1; j <= n; j++)
            dp[n-1][j] = dis[n][n-1] + dis[n][j];
        for(int i = n-2; i >= 1; i--)
        {
            for(int j = 1; j <= n; j++)
            {
                dp[i][j] = min(dp[i+1][j]+dis[i][i+1], dp[i+1][i]+dis[j][i+1]);
            }
        }
        printf("%.2lf\n", dp[1][1]);
    }
    return 0;
}

D-单向TSP问题

题意

给出二维数组,从最左一列任意位置出发每次往右、右上或者右下的方向移动,到最后的一列。需要注意的是,第一行的右上为最后一行对应的位置,同理,最后一行的情况相同。
每一个都有三种决策的情况。思路比较简单,关键实现。还有就是要在开一个数组来打印路径。
看代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <string>
#include <bitset>
#include <cstdio>
#include <limits>
#include <vector>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <fstream>
#include <numeric>
#include <sstream>
#include <iostream>
#include <algorithm>
#define MEM(a,x) memset(a,x,sizeof(a))
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int INF = 999999999;
int a[105][105], dp[105][105], p[105][105];
int main()
{
//    freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n, m;
    while(cin >> n >> m)
    {
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
            {
               cin >> a[i][j];
               dp[i][j] = INF;
            }
        for(int i = 0; i < n; i++)
            dp[i][m-1] = a[i][m-1];
        for(int j = m-2; j >=0; j--)
        {
            for(int i = 0; i < n; i++)
            {
                int c[] = {i-1, i, i+1};
                if(i == 0) c[0] = n-1;
                if(i == n-1) c[2] = 0;
                sort(c, c+3);
//                for(int k = 1; k < 3; k++)
//                    cout << c[k] << " ";
//                cout << endl;
                dp[i][j] = INF;
                for(int k = 0; k < 3; k++)
                {
                    int v = dp[c[k]][j+1] + a[i][j];
                    if(dp[i][j] > v)
                    {
                        dp[i][j] = v;
                        p[i][j] = c[k];
                    }
                }
            }
        }
//        for(int i = 0; i < n; i++)
//        {
//             for(int j = 0; j < m-1; j++)
//               cout << p[i][j] << " ";
//               cout << endl;
//        }
        int ans = 999999999;
        int first = 0;
        for(int i = 0; i < n; i++)
        {
            if(ans > dp[i][0])
            {
                ans = dp[i][0];
                first = i;
            }
        }
        printf("%d", first+1);
        for(int i = p[first][0], j = 1; j < m; i = p[i][j], j++) printf(" %d", i+1);
        cout << endl << ans << endl;
    }
    return 0;
}

F - Lighting System Design

题意

设计一个照明系统,一共有n种灯泡进行选择。不同种类的灯泡必须用不同的电源,但同一种灯泡却可以共用一个电源。每种灯泡给定:电压,电源费用,每个灯泡的费用和所需灯泡数。同时可以把一些灯泡换成电压更高的另一种灯泡来节省电源的费用,计算出最优的花费。

分析

贪心的思考,每种电压的灯泡要换就全换。
先将所用种类的灯泡按照电压值进行从小到大的排序,每种灯泡都是可以将前面的灯泡替换成本身的灯泡,所以可以dp(i) 表示截止到第i种灯泡的最优花费。
dp(i) = min{dp(j) + (j~i num) * price[i] + k [i]} ,同时也可以用前缀数组进行优化。

代码

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <string>
#include <bitset>
#include <cstdio>
#include <limits>
#include <vector>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <fstream>
#include <numeric>
#include <sstream>
#include <iostream>
#include <algorithm>
#define MEM(a,x) memset(a,x,sizeof(a))
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int maxn = 1005;
const int INF = 1000000000;
struct L
{
    int v, k, cost, num;
};
L l[maxn];
int dp[maxn], s[maxn];
bool cmp(L a, L b)
{
    return a.v < b.v;
}
int main()
{
//    freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int n;
    while(cin >> n && n)
    {
        for(int i = 1; i <= n; i++)
        {
            cin >> l[i].v >> l[i].k >> l[i].cost >> l[i].num;
        }
        sort(l+1, l+n+1, cmp);
        s[0] = 0;
        for(int i = 1; i <= n; i++)
            s[i] = s[i-1] + l[i].num;
        dp[0] = 0;
        for(int i = 1; i <= n; i++)
        {
            dp[i] = s[i] * l[i].cost + l[i].k;
            for(int j = 0; j < i; j++)
            {
                int v = dp[j] + (s[i] - s[j])*l[i].cost + l[i].k;
                dp[i] = min(dp[i], v);
            }
        }
        cout << dp[n] << endl;
    }
    return 0;
}

G- Partitioning by Palindromes

题意

输入一个由小写字母组成的字符串,划分成尽量少的回文串。

分析

设dp(i) 表示 0~ i 划分成最小回文串的个数。
状态转移方程为:
dp(i) = min{ dp(j) + 1 | s[j+1…i] 是回文串};
边界: dp(i) = i+1;
在实现的时候,注意对开头就能构成回文串的个数进行特判为1.

代码

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <string>
#include <bitset>
#include <cstdio>
#include <limits>
#include <vector>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <fstream>
#include <numeric>
#include <sstream>
#include <iostream>
#include <algorithm>
#define MEM(a,x) memset(a,x,sizeof(a))
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int INF = 10000000;
const int maxn = 1000+10;
int p[maxn][maxn], dp[maxn];
string s;
bool is_p(int l, int r)
{
    while(l <= r)
    {
        if(s[l] != s[r]) return false;
        l++;
        r--;
    }
    return true;
}
int main()
{
//    freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int t;
    cin >> t;
    while(t--)
    {
        cin >> s;
        int n = s.size();
        for(int i = 0; i < n; i++)
        {
            for(int j = 0; j < n; j++)
            {
                p[i][j] = is_p(i,j);
            }
        }
        for(int i = 0; i < n; i++) dp[i] = i+1;
        for(int i = 1; i < n; i++)
        {
            if(p[0][i]) dp[i] = 1;
            for(int j = 1; j <= i; j++)
            {
                if(p[j][i])
                {
//                    cout << dp[j] << endl;
                    dp[i] = min(dp[i], dp[j-1]+1);
                }
            }
        }
        cout << dp[n-1] << endl;
    }
    return 0;
}

OK。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值