• 2018.02.04(补作业系列)


    2018.02.04

    补作业系列

    1.合并石子

    思路:如解析所示,对于动态规划的题目,只要有了思路,就容易了。不过一本通上的此题比超链接的此题多了一个条件:每次只能合并相邻的两堆石子。所以写状态转移方程就更容易了。

    核心代码:

     1 #include <stdio.h>
     2 #include <math.h>
     3 #include <string.h>
     4 int _Min(int x,int y){return x<y?x:y;}
     5 int f[101][101];
     6 int s[101];
     7 int n,i,j,k,x;
     8 int main(){
     9     scanf("%d",&n);
    10     for(i=1;i<=n;i++){
    11         scanf("%d",&x);
    12         s[i]=s[i-1]+x;
    13     }
    14     memset(f,20000,sizeof(f));
    15     for(i=1;i<=n;i++)f[i][i]=0;
    16     for(i=n-1;i>=1;i--){
    17         for(j=i+1;j<=n;j++){
    18             for(k=i;k<=j-1;k++)
    19                 f[i][j]=_Min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
    20         }
    21     }
    22     printf("%d
    ",f[1][n]);
    23     return 0;
    24 }
    View Code

    状态转移方程&解析:s[i]表示前i堆石子的数量总和,f[i][j]表示把第i堆石子到第j堆石子合并成一堆的最优值。

    for ( i = n-1 ; i >= 1 ; i-- ){
        for ( j = i+1 ; j <= n ; j++ ){
            for ( k = i ; k <= j-1 ; k++ ){
                f[i][j] = min ( f[i][j] , f[i][k] + f[k+1][j] + s[j] -s[i-1] );
            }
        }
    }
    输出f[1][n]

      

    状态:AC

    2.挖地雷

    思路:如解析所示,最值得注意的是该题对于状态和阶段的定义,只要将此概念明确,写代码就很容易了。

    核心代码:

     1 #include <stdio.h>
     2 #include <math.h>
     3 #include <string.h>
     4 int main(){
     5     long f[201]={0},w[201],c[201]={0};
     6     int a[201][201]={0}; 
     7     long i,j;
     8     long n;
     9     long x,y;
    10     long l,k,maxx;
    11     memset(f,0,sizeof(f));
    12     memset(c,0,sizeof(c));
    13     memset(a,0,sizeof(a));
    14     scanf("%d",&n);
    15     for(i=1;i<=n;i++)
    16         scanf("%ld",&w[i]);
    17     for(i=1;i<n;i++){
    18         for(j=i+1;j<=n;j++){
    19             scanf("%d",&x);
    20             if(x==1)a[i][j]=1;
    21         }
    22     }
    23     f[n]=w[n];
    24     for(i=n-1;i>=1;i--){
    25         l=0;k=0;
    26         for(j=i+1;j<=n;j++)
    27             if((a[i][j])&&(f[j]>l)){
    28                 l=f[j];k=j;
    29             }
    30         f[i]=l+w[i];
    31         c[i]=k;
    32     }
    33     k=1;
    34     for(j=2;j<=n;j++)
    35         if(f[j]>f[k])k=j;
    36     maxx=f[k];
    37     printf("%ld",k);
    38     k=c[k];
    39     while(k!=0){
    40         printf(" %ld",k);
    41         k=c[k];
    42     }
    43     printf("
    %ld
    ",maxx);
    44     return 0;
    45 }
    View Code

    状态转移方程&解析:

    很明显,题目所规定的所有路径都是单向的,所以满足无后效性原则和最优化原理。设w[i]为第i个地窖所藏有的地雷数,a[i][j]表示第i个地窖与第j个地窖之间是否有通路,f[i]为从第i个地窖开始最多可以挖出的地雷数,则有如下递归式:

    f[i]=max{ w[i]+f[j] }
    ( i<j<=n  &&  a[i][j]=true )
    边界: f[n]=w[n]

      

    于是就可以通过递推的方法,从后面的f(n)往前逐个找出所有的f[i],再从中找出一个最大的即为问题二的解。对于具体走的路径(问题一),可以通过一个向后的链接来实现。

    状态:AC

    3.友好城市

    思路:对于南岸和北岸的两组坐标,把其中一组进行排序后,会发现其实就是求另外一组的最长不下降序列。但是排序的话,数组行不通,要STL的结构体排序,还是不知道排。抄了一个题解,依葫芦画瓢打出来了。据说还有一个O(n logn)的做法,没时间研究了,下次再说。

    核心代码:

     1 #include <cstdio>
     2 #include <cmath>
     3 #include <cstring>
     4 #include <iostream>
     5 #include <algorithm>
     6 using namespace std;
     7 struct data{
     8     int south;
     9     int north;
    10 };
    11 int sum[200001];
    12 int n,maxx=1;
    13 bool cmp(data a,data b){return a.south<b.south;}
    14 int _Max(int x,int y){return x>y?x:y;}
    15 int main(){
    16     struct data city[200001];
    17     scanf("%d",&n);
    18     int i,j;
    19     
    20     for(i=1;i<=n;i++)
    21         scanf("%d%d",&city[i].south,&city[i].north);
    22     
    23     for(i=0;i<=n;i++)
    24         sum[i]=1;
    25     
    26     sort(city+1,city+n+1,cmp);
    27     for(i=1;i<=n;i++){
    28         for(j=i+1;j<=n;j++){
    29             if(city[i].north<=city[j].north){
    30                 sum[j]=_Max(sum[j],sum[i]+1);
    31             }
    32         }
    33         maxx=_Max(maxx,sum[i]);
    34     }
    35     printf("%d
    ",maxx);
    36     return 0;
    37 }
    View Code

    状态转移方程&解析:

    我们将每对友好城市看成一条线段,则这道题的描述化为:有N条线段,问最少去掉多少条线,可以使剩下的线段互不交叉?以北岸为线的起点而南岸为线的终点;先将所有的线按照起点坐标值从小到大排序,可以发现:只要线J的起点小于线I的起点,同时它的终点也小于线I的终点,则这两条线不相交。因此,求所有线中最多能有多少条线不相交,实际上就是从终点坐标值序列中求一个最长不下降子序列。这就把题目转换成非常经典的动态规划题目了。状态转移方程如下:

    L[i]=max{ L[j] } +1 ; 1<=j<i  且  Sj < Si ,Si 为航线的终点坐标值

      

    状态:AC

    4.方格取数

    思路:用四重循环,模拟两条路同时走的所有四种情况,(其实就是数字三角形的升升升级版)用四维数组保存最优情况,最后输出sum[n][n][n][n]。

    核心代码:

     1 #include <stdio.h>
     2 #include <math.h>
     3 #include <string.h>
     4 int _Max(int x,int y){return x>y?x:y;}
     5 int a[51][51],sum[51][51][51][51];
     6 int n,i,j;
     7 int h,k,x,y,z;
     8 int main(){
     9     scanf("%d%d%d%d",&n,&x,&y,&z);
    10     while(x&&y&&z){
    11         a[x][y]=z;
    12         scanf("%d%d%d",&x,&y,&z);
    13     }
    14     for(i=1;i<=n;i++){
    15         for(j=1;j<=n;j++){
    16             for(h=1;h<=n;h++){
    17                 for(k=1;k<=n;k++){
    18                     int tmp1=_Max(sum[i-1][j][h-1][k],sum[i][j-1][h][k-1]);
    19                     int tmp2=_Max(sum[i][j-1][h-1][k],sum[i-1][j][h][k-1]);
    20                     sum[i][j][h][k]=_Max(tmp1,tmp2)+a[i][j];
    21                     if(i!=h&&j!=k)sum[i][j][h][k]+=a[h][k];
    22                 }
    23             }
    24         }
    25     }
    26     printf("%d
    ",sum[n][n][n][n]);
    27     return 0;
    28 }
    View Code

    状态转移方程&解析:

    一个四重循环枚举两条路分别走到的位置。由于每个点均从上或左继承而来,故内部有四个if,分别表示两个点从上上,上左,左上,左左继承来时,加上当前两个点所取得的最大值。a[i][j]表示 ( i , j ) 格上的值,sum[i][j][h][k]表示第一条路走到 ( i , j ),第二条路走到 ( h , k ) 时的最优解。当( i , j ) != ( h , k )时:sum[i][j][h][k]=max( sum[i-1][j][h-1][k] , sum[i][j-1][h][k-1] , sum[i][j-1][h-1][k] , sum[i-1][j][h][k-1] ) + a[i][j] + a[h][k]。当( i , j ) == ( h , k )时:sum[i][j][h][k]=max( sum[i-1][j][h-1][k] , sum[i][j-1][h][k-1] , sum[i][j-1][h-1][k] , sum[i-1][j][h][k-1] ) + a[i][j] 。

    状态:AC

    5.乘积最大

    思路:对于状态的定义是这样的:f[i][k]表示在前i位数中插入k个乘号所得的最大值,a[j][i]表示从第j位到第i位所组成的自然数。

    核心代码:

     1 #include <stdio.h>
     2 #include <math.h>
     3 #include <string.h>
     4 long long a[41][41],f[41][41];
     5 long long s;
     6 int n,k,k1;
     7 int i,j;
     8 int _Max(int x,int y){return x>y?x:y;}
     9 int main(){
    10     scanf("%d%d",&n,&k1);
    11     scanf("%lld",&s);
    12     for(i=n;i>=1;i--){
    13         a[i][i]=s%10;
    14         s/=10;
    15     }
    16     for(i=2;i<=n;i++)
    17         for(j=i-1;j>=1;j--)
    18             a[j][i]=a[j][i-1]*10+a[i][i];
    19     for(i=1;i<=n;i++)
    20         f[i][0]=a[1][i];
    21     for(k=1;k<=k1;k++)
    22         for(i=k+1;i<=n;i++)
    23             for(j=k;j<i;j++)
    24                 f[i][k]=_Max(f[i][k],f[j][k-1]*a[j+1][i]);
    25     printf("%lld
    ",f[n][k1]);
    26     return 0;
    27 }
    View Code

    状态转移方程&解析:

    我们把它按插入的乘号来划分阶段,若插入k个乘号,可把问题看作是k个阶段的决策问题。设f[i][k]表示在前i位数中插入k个乘号所得的最大值,a[j][i]表示从第j位到第i位所组成的自然数。用f[i][k]储存阶段k的每一个状态,可以获得状态转移方程:

    f[i][k] = max { f[j][k-1] * a[j+1][i] } ( k <= j < i )
    边界值:
    f[j][0]=a[1][j] ( 1 <= j <= n )

      

    根据状态转移方程,我们就可以很容易写出动态规划程序:

    for (j = 1 ; k a<= k1 ; k++)
        for (i= k + 1 ; i <= n ; i++)
            for (j = k ; j < i ; j++)
                f[i][k] = max( f[i][k] , f[j][k-1] * a[j+1][i] ); 
    

      

    状态:UNAC(要用高精度,long long只有20分)

    6.公共子序列

    思路:...以后再补吧

    核心代码:

     1 #include <cstdio>
     2 #include<cmath>
     3 #include<cstring>
     4 #include<iostream>
     5 using namespace std;
     6 int main(){
     7     char a[210],b[210];
     8     int f[201][201];
     9     int l1,l2,i,j;
    10     while(cin>>a+1>>b+1){
    11         memset(f,0,sizeof(f));
    12         l1=strlen(a);
    13         l2=strlen(b);
    14         for(i=1;i<l1;i++)
    15             for(j=1;j<l2;j++)
    16                 if(a[i]==b[j]&&f[i-1][j-1]+1>f[i][j])
    17                     f[i][j]=f[i-1][j-1]+1;
    18                 else
    19                     f[i][j]=max(f[i-1][j],f[i][j-1]);
    20         cout<<f[l1-1][l2-1]<<endl;
    21     }
    22     return 0;
    23 }
    View Code

    状态:AC

  • 相关阅读:
    Object有哪些方法?
    去除掉myeclipse2017页面右上角的图片
    报错 IllegalArgumentException occurred calling getter of cn.itcast.entity.Customer.cid
    如果在applicationContext.xml中没有配置bean的属性,那么也会导致空指针异常
    报错HTTP Status 500
    srm开发(基于ssh)(4)
    报错HTTP Status 500
    HTTP Status 500
    数字
    算法总结——树状数组
  • 原文地址:https://www.cnblogs.com/yzyl-Leo-wey/p/ZuoYeZhongYuBuWanLa.html
Copyright © 2020-2023  润新知