第九章 动态规划初步

9.2DGA上的动态规划

例题9-1 城市里的间谍 uva1025

主算法是dp,有个预处理数组就是看往左环视往右的时候在时刻i的车站j上有没有车

#include<bits/stdc++.h>
using namespace std;
int dp[220][55];
int visit[220][55][2];
int t[220],d1[55],d2[55];
const int INF = 0x3f3f3f3f;
int main()
{
    int n,T;
    int M1,M2;
    int k=1;
    while(~scanf("%d",&n)&&n){
        scanf("%d",&T);
        for(int i=1;i<n;i++) scanf("%d",&t[i]);
        scanf("%d",&M1);
        for(int i=1;i<=M1;i++) scanf("%d",&d1[i]);
        scanf("%d",&M2);
        for(int i=1;i<=M2;i++) scanf("%d",&d2[i]);
        memset(visit,0,sizeof(visit));
        for(int i=1;i<=M1;i++) {
            visit[d1[i]] [1][0]=1;
            int temp=d1[i];
            for(int j=1;j<n;j++){
                temp+=t[j];
                if(temp<=T) visit[temp][j+1][0]=1;
                else break;
            }
        }
          for(int i=1;i<=M2;i++) {
            visit[d2[i]] [n][1]=1;
            int temp=d2[i];
            for(int j=n-1;j>=1;j--){
                temp+=t[j];
                if(temp<=T) visit[temp][j][1]=1;
                else break;
            }
        }
        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&&visit[i][j][0]&&i+t[j]<=T)
                    dp[i][j]=min(dp[i][j],dp[i+t[j]][j+1]);
                if(j>1&&visit[i][j][1]&&i+t[j-1]<=T)
                    dp[i][j]=min(dp[i][j],dp[i+t[j-1]][j-1]);
            }
        }
        if(dp[0][1]>=INF)
            printf("Case Number %d: impossible\n",k++);
        else printf("Case Number %d: %d\n",k++,dp[0][1]);

    }
    return 0;
}
例题9-2 巴比伦塔 uva437

DAG不固定起点的最长路径算法:

d(i)表示从节点i处罚的最长路长度,第一步只能走到它相邻的点 d(i)=max(d(j)

<pre name="code" class="cpp">#include<bits/stdc++.h>
using namespace std;
int G[100][100];
int d[100];
int n,m;
//
int dp(int i)
{
    int &ans=d[i];
    if(ans > 0) return ans;
    ans=1;
    for(int j=1;j<=n;j++)
        if(G[i][j]) ans = max(ans,dp(j)+1);
    return ans;
}
//输出字典序最小的路径
void print_ans(int i)
{
    printf("%d ",i);
    for(int j=1;j<=n;j++) if(G[i][j]&&d[i]==d[j]+1)
    {
        print_ans(j);
        break;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    while(m--){
        int x,y;
        scanf("%d%d",&x,&y); G[x][y]=1;
    }
    memset(d,0,sizeof(d));
    int maxx=0,I;
    //输出的是最长长度
    for(int i=1;i<=n;i++) dp(i);
    for(int i=1;i<=n;i++){
        printf("%d ",d[i]);
        if(d[i]>maxx) { I=i; maxx=max(d[i],maxx); }
    }
    print_ans(I);

    return 0;
}
/*
5 4
1 2
1 3
3 4
4 5
4
5
4
1 2
2 3
3 4
4 5
5
*/


 uva437: 

#include<bits/stdc++.h>
using namespace std;
struct node{
    int x,y,z;
    void f(int a,int b,int c){ x=a,y=b,z=c; }
}st[200];
bool cmp(node a,node b){
    if(a.x*a.y < b.x*b.y) return 1; return 0;
}
int n,m,x,y,z,dp[200];
int main()
{
    int flag=1;
    while(scanf("%d",&n)&&n){
        m=0;
        int i,j;
        for(i=0;i<n;i++){
            scanf("%d%d%d",&x,&y,&z);
            st[ m++].f(x,y,z);     //将6种立方体均保存起来
            st[ m++].f(x,z,y);
            st[ m++].f(y,z,x);
            st[ m++].f(y,x,z);
            st[ m++].f(z,x,y);
            st[ m++].f(z,y,x);
        }
        sort(st,st+m,cmp);
        int t=0;
        for(i=0;i<m;i++){
            dp[i]=st[i].z;
            for(j=0;j<i;j++)
                if(st[i].x>st[j].x &&st[i].y>st[j].y)
                dp[i]=max(dp[i],dp[j]+st[i].z);
                if(dp[i]>t) t=dp[i];
        }
        printf("Case %d: maximum height = %d\n",flag++,t);
    }
    return 0;
}

例题9-3 旅行  uva1347

平面上有n个坐标均为正数的点,按照x坐标从小到大一次给出。求一条最短路线,从最左边的点出发到最右边的点,再回到最左边的点。除了第一个和最右一个点其他点恰好只经过一次。

分析:

可以等效为两个人从第一个点出发,沿不同的路径走到最右点。

d(i, j)表示点1~max(i, j)这些点全部都走过,而且两人的位置分别是i和j,最少还需要走多长的距离。由这个定义可知,d(i, j) == d(j, i),所以我们再加一个条件,d(i, j)中i>j

这样状态d(i, j)只能转移到d(i+1, j)和d(i+1, i)

边界:d(n-1, j) = dist(n-1, n) + dist(j, n) (1 ≤ j < n-1)

最终所求答案就是dist(1, 2) + d(1, 2)



多阶段决策问题:

多段图的最短路

例题9-4 单向TSP uva116


01背包问题

物品无限的背包问题

固定终点的最长路和最短路

int dp(int S){
    if(vis[S]) return d[S]; vis[S]=1;
    int& ans=d[S];
    ans =1(1<<30);
    for(int i=1;i<=n;i++) if(S>=V[i]) ans=max(ans,dp[S-V[i]]+1);
    return ans;
}

记忆化搜索递推求最大最小两个值

const int INF = 0x3f3f3f3f;
minv[0]=maxv[0]=0;
for(int i=1;i<=S;i++) minv[i]=INF,maxv[i]=-INF;
for(int i=1;i<=S;i++)
    for(int j=1;j<=n;j++)
if(i>=V[j]) {
    minv[i]=min(minv[i],min[i-V[j] ]+1);
    maxv[i]=min(maxv[i],max[i-V[j] ]+1);
}
printf("%d %d\n",minv[S],maxv[S]);
//输出字典序最小方案
void print_ans(int *d,int S){
    for(int i=1;i<=n;i++)
    if(S>=V[i]&&d[S]==d[S-V[i]]+1){
        printf("%d",i);
        print_ans(d,s-v[i]);
        break;
    }
}
时间换空间,减少了输出时的循环

for(int i=1;i<=S;i++)
    for(int j=1;j<=n;j++)
if(i>=V[j]){
    if(min[i]>minp[i-V[j]]+1){
        min[i]=min[i-v[j] ]+1;
        min_coin[i]=j;
    }
    if(max[i]<max[i-V[j] ]+1){
        max[i]=max[i-V[j] ]+1;
        max_coin[ij;
    }
}
void print_ans(int *d,int S){
    while(S){
        printf("%d",d[S]); S-=V[d[S] ];
    }
}

例题9-5 金歌劲曲 uva12563

#include<bits/stdc++.h>
using namespace std;
int n,t;
int v[55];
int d[55][10000];
int path[55][10000];
int main()
{
    int tt;
    scanf("%d",&tt);
    for(int kase=1;kase<=tt;kase++)
    {
        scanf("%d%d",&n,&t);
        memset(path,0,sizeof(path));
        memset(d,0,sizeof(d));
        memset(v,0,sizeof(v));
        for(int i=1;i<=n;i++) scanf("%d",&v[i]);
        for(int i=n;i>=1;i--)
            for(int j=0;j<=t;j++)
        {
            d[i][j]=(i==n)?0:d[i+1][j];
            path[i][j]=path[i+1][j];
            if(j>=v[i]){
                if(d[i][j]<d[i+1][j-v[i]]+1)
                {
                    path[i][j]=v[i]+path[i+1][j-v[i] ];
                    d[i][j]=d[i+1][j-v[i] ]+1;
                }
                else if(d[i][j]==d[i+1][j-v[i] ]+1)
                    path[i][j]=max(path[i+1][j-v[i] ]+v[i],path[i+1][j]);
            }
        }
        int max_=-1;
    for(int i=t-1;i>=0;i--)
    {
      if(d[1][i]==d[1][t-1])
      {
        if(max_<path[1][i])
          max_=path[1][i];
      }
    }
    printf("Case %d: %d %d\n",kase,d[1][t-1]+1,max_+678);

    }
    return 0;
}
更多经典模型

最长上升子序列问题LIS

最长公共子序列问题LCS

例题9-6 照明系统设计 uva11400

#include<bits/stdc++.h>
using namespace std;
#define maxn 1010
struct lamp{
    int v,k,c,l;
}a[maxn];
int n,v1,k1,c1,l1;
int s[maxn];
int d[maxn];
bool cmp(lamp a1,lamp a2){
    return a1.v<a2.v;
}
int main()
{
    while(scanf("%d",&n)!=EOF){
        if(n==0) break;
        memset(s,0,sizeof(s));
        memset(d,0,sizeof(d));
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d%d%d",&v1,&k1,&c1,&l1);
            a[i].v=v1;
            a[i].k=k1;
            a[i].c=c1;
            a[i].l=l1;
        }
        sort(a+1,a+n+1,cmp);
        s[0]=0;
        for(int i=1;i<=n;i++)
        {
            s[i]=s[i-1]+a[i].l;
        }
        d[0]=0;
        for(int i=1;i<=n;i++)
            for(int j=0;j<i;j++)
        {
            if(j==-0) d[i]=(s[i])*a[i].c+a[i].k;
            else d[i]=min(d[j]+(s[i]-s[j])*a[i].c+a[i].k,d[i]);
        }
        printf("%d\n",d[n]);
    }
    return 0;
}


例题9-7划分回文串  uva11584

加一个判断s[i~j]是否是回文串的预处理,把复杂度降到n*n、

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1010
char str[MAXN];
int f[MAXN];
bool isPalind(int l,int r){
    while(l<r){
        if(str[l]!=str[r]) return false;
        ++l; --r;
    }
    return true;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%s",str+1);
        int len=strlen(str+1);
        memset(f,0,sizeof(f));
        for(int i=1;i<=len;i++)
        {
            f[i]=i+1;
            for(int j=1;j<=i;j++) if(isPalind(j,i)){
                f[i]=min(f[i],f[j-1]+1);
            }
        }
        printf("%d\n",f[len]);
    }
    return 0;
}
例题9-8 颜色的长度 uva1625

最优矩阵链乘


最优子结构:

f(i,j)=min{f(i,k)+f(k+1,j)+pi-1pkpj}

最优三角剖分

例题9-9 切木棍 uva10003

#include<stdio.h>
#include<string.h>
const int INF=99999999;
const int MN=60;
int dp[MN][MN];
int num[MN];

int main()
{
    int n,m,i,j,L,p,k,tmp;
    int MIN;
    while(scanf("%d",&L) && L)
    {
        scanf("%d",&n);
        for(i=1;i<=n;i++) scanf("%d",&num[i]);
        num[0]=0;
        num[n+1]=L;
        memset(dp,0,sizeof(dp));
        for(p=1;p<=n+1;p++)
           for(i=0;i<=n+1;i++)
           {
               j=i+p;
               MIN=INF;
               if(j>n+1) break;//这个很重要
               for(k=i+1;k<j;k++)//这里是i+1,原先令k=i是错的,因为dp[i][i]=0,这必然导致找到的值小于正常的
               {
                   tmp=dp[i][k]+dp[k][j]+num[j]-num[i];
                   if(MIN>tmp) MIN=tmp;
               }
               if(MIN!=INF)dp[i][j]=MIN;
           }
        printf("The minimum cutting is %d.\n",dp[0][n+1]);

    }
    return 0;
}

例题9-10 括号序列 Uva1626


树上的动态规划

树的最大独立集



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值