zoukankan      html  css  js  c++  java
  • 算法复习——区间dp

    感觉对区间dp也不好说些什么直接照搬讲义了2333

    例题:

    1.引水入城(洛谷1514)

    这道题先开始看不出来到底和区间dp有什么卵关系····

    首先肯定是bfs暴力判一判可以覆盖到哪些城市····无解直接输出···有解得话就要想想了····

    这道题关键是要发现··如果一个蓄水池所在城市可以覆盖到一些沙漠城市···那么这些沙漠城市肯定是一段连续区间····不然假设有一个城市是断开的而两边都被同一个蓄水池流出的水覆盖,这个城市四周的城市都肯定比它矮···(不理解举个反例吧···反正我举不出来)···然后就可以找到每一个蓄水池它对应的可以覆盖的区间···转化成一个关于线段覆盖的区间dp问题·····即可求解(dp方程看代码吧)

    另外注意这道题bfs时注意是入队时打标记不然会爆···还有判条件时先判越界没有···

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<ctime>
    #include<cctype>
    #include<cstring>
    #include<string>
    #include<algorithm>
    using namespace std;
    const int inf=0x3f3f3f3f;
    const int N=1005;
    const int gox[4]={1,0,-1,0};
    const int goy[4]={0,1,0,-1};
    struct node
    { 
      int x,y;
    }que[N*N];
    struct node1
    {
      int l,r;
    }g[N];
    int map[N][N],n,m,tail,head,f[N];
    bool visit[N][N];
    inline int R()
    {
      char c;int f=0;
      for(c=getchar();c<'0'||c>'9';c=getchar());
      for(;c<='9'&&c>='0';c=getchar())
        f=(f<<3)+(f<<1)+c-'0';
      return f;
    }
    inline void bfs()
    {
      head=1,tail=0;
      for(int i=1;i<=m;i++)
      {
        que[++tail].x=1;que[tail].y=i;
        visit[que[head].x][que[head].y]=true;
      }
      for(;head<=tail;head++)
      {
        for(int i=0;i<=3;i++)
        {
          visit[que[head].x][que[head].y]=true;
          int vx=que[head].x+gox[i];int vy=que[head].y+goy[i];
          if(vx<1||vx>n||vy<1||vy>m||visit[vx][vy]||map[vx][vy]>=map[que[head].x][que[head].y])  continue;
          node temp; 
          temp.x=vx,temp.y=vy;que[++tail]=temp;
          visit[temp.x][temp.y]=true;
        }
      }
    }
    inline void dfs(int x,int y,int u)
    {
      visit[x][y]=true;
      if(x==n)
        g[u].l=min(g[u].l,y),g[u].r=max(g[u].r,y);
      for(int i=0;i<=3;i++)
      {
        int vx=x+gox[i];int vy=y+goy[i];
        if(visit[vx][vy]||vx<1||vx>n||vy<1||vy>m||map[vx][vy]>=map[x][y])  continue;
        dfs(vx,vy,u);
      }
    }
    int main()
    {
      //("a.in","r",stdin);
      memset(f,inf,sizeof(f));
      n=R(),m=R();
      for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
          map[i][j]=R();
      bfs();
      int temp=0;
      for(int i=1;i<=m;i++)
        if(visit[n][i]==true)  temp++;
      if(temp!=m)
      {
        cout<<"0"<<endl;
        cout<<m-temp<<endl;
        return 0;
      }
      for(int i=1;i<=m;i++)
      {
        memset(visit,false,sizeof(visit));
        g[i].l=m+1;g[i].r=0;
        dfs(1,i,i);
      }
      f[0]=0;
      for(int i=1;i<=m;i++)
        for(int j=1;j<=m;j++)
          if(g[j].l<=i&&g[j].r>=i)
            f[i]=min(f[i],f[g[j].l-1]+1);
      cout<<"1"<<endl;
      cout<<f[m]<<endl;
      return 0;
    } 

    2.压缩(bzoj1068)

    Description

      给一个由小写字母组成的字符串,我们可以用一种简单的方法来压缩其中的重复信息。压缩后的字符串除了小
    写字母外还可以(但不必)包含大写字母R与M,其中M标记重复串的开始,R重复从上一个M(如果当前位置左边没
    有M,则从串的开始算起)开始的解压结果(称为缓冲串)。 bcdcdcdcd可以压缩为bMcdRR,下面是解压缩的过程

     

      另一个例子是abcabcdabcabcdxyxyz可以被压缩为abcRdRMxyRz。

    Input

      输入仅一行,包含待压缩字符串,仅包含小写字母,长度为n。

    Output

      输出仅一行,即压缩后字符串的最短长度。

    Sample Input

    bcdcdcdcdxcdcdcdcd

    Sample Output

    12

    HINT

    在第一个例子中,解为aaaRa,在第二个例子中,解为bMcdRRxMcdRR。 

    【限制】 

    100%的数据满足:1<=n<=50 100%的数据满足:1<=n<=50

    从这道题可以看出:区间dp不只可以用于解决序列的划分、覆盖问题,还可以用于解决序列的压缩、消除问题·····

    解决这一类的通法是枚举左右区间然后再在中间枚举断点

    接下来引用DaD3zZ的题解,%%%%%

    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<ctime>
    #include<cctype>
    #include<cstring>
    #include<string>
    #include<algorithm>
    using namespace std;
    const int N=105;
    int f[N][N][2],n;
    char s[N];
    inline bool check(int l,int r)
    {
      if((r-l+1)%2==1)  return false;
      int mid=(l+r)/2;
      for (int i=l; i<=mid; i++) if (s[i]!=s[mid+i-l+1]) return false;
      return true;
    }
    int main()
    {
      //freopen("a.in","r",stdin);
      scanf("%s",s+1);
      n=strlen(s+1);
      for(int i=n;i>=1;i--)
        for(int j=i;j<=n;j++)
        {
          f[i][j][0]=f[i][j][1]=j-i+1;
          for(int k=i;k<j;k++)
          {  
            f[i][j][1]=min(f[i][j][1],min(f[i][k][0],f[i][k][1])+min(f[k+1][j][0],f[k+1][j][1])+1);
            f[i][j][0]=min(f[i][j][0],f[i][k][0]+j-k);
          }
          if(check(i,j))  f[i][j][0]=f[i][(j+i)/2][0]+1; 
          if(j-i+1==1)  f[i][j][1]=n+1;
        }
      printf("%d
    ",min(f[1][n][1],f[1][n][0]));
      return 0;
    }

    3.字符串折叠(bzoj1090 SCOI2003)

    Description

    折叠的定义如下: 1. 一个字符串可以看成它自身的折叠。记作S  S 2. X(S)是X(X>1)个S连接在一起的串的折叠。记作X(S)  SSSS…S(X个S)。 3. 如果A  A’, BB’,则AB  A’B’ 例如,因为3(A) = AAA, 2(B) = BB,所以3(A)C2(B)  AAACBB,而2(3(A)C)2(B)AAACAAACBB 给一个字符串,求它的最短折叠。例如AAAAAAAAAABABABCCD的最短折叠为:9(A)3(AB)CCD。

    Input

    仅一行,即字符串S,长度保证不超过100。

    Output

    仅一行,即最短的折叠长度。

    Sample Input

    NEERCYESYESYESNEERCYESYESYES

    Sample Output

    14

    HINT

    一个最短的折叠为:2(NEERC3(YES))

    这道题和例题2几乎是一样的,同样的区间压缩dp同样的枚举左右区间,同样的枚举断点,同样的判断压缩合法性,并且状态转移还要简单一点·······就不多说了

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<ctime>
    #include<cctype>
    #include<cstring>
    #include<string>
    #include<algorithm>
    using namespace std;
    const int N=105;
    int n,f[N][N],m;
    char s[N];
    inline bool check(int a,int b,int c)
    {
      if((c-a+1)%(b-a+1)) return false;
      int num=(c-a+1)/(b-a+1);m=0;
      for(int i=a;i<=b;i++)
        for(int j=i+(b-a+1);j<=c;j+=(b-a+1))  
          if(s[j]!=s[i])  return false;  
      while(num)  m++,num/=10;
      return true;
    }
    int main()
    {
      //freopen("a.in","r",stdin);
      scanf("%s",s+1);n=strlen(s+1);
      for(int i=n;i>=1;i--)
        for(int j=i;j<=n;j++)
        {
          f[i][j]=j-i+1;
          if(i==j)  continue;
          for(int k=i;k<j;k++)
          {
            if(check(i,k,j))  f[i][j]=min(f[i][j],f[i][k]+m+2);
            f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
          }
        }
      cout<<f[1][n]<<endl;
      return 0;
    }

     4.Multiplication Puzzle(zoj1602 poj1651)

    The multiplication puzzle is played with a row of cards, each containing a single positive integer. During the move player takes one card out of the row and scores the number of points equal to the product of the number on the card taken and the numbers on the cards on the left and on the right of it. It is not allowed to take out the first and the last card in the row. After the final move, only two cards are left in the row.

    The goal is to take cards in such order as to minimize the total number of scored points.

    For example, if cards in the row contain numbers 10 1 50 20 5, player might take a card with 1, then 20 and 50, scoring
    10*1*50 + 50*20*5 + 10*50*5 = 500+5000+2500 = 8000

    If he would take the cards in the opposite order, i.e. 50, then 20, then 1, the score would be
    1*50*20 + 1*20*5 + 10*1*5 = 1000+100+50 = 1150.


    Input

    The first line of the input file contains the number of cards N (3 <= N <= 100). The second line contains N integers in the range from 1 to 100, separated by spaces.

    Process to the end of file.


    Output

    Output file must contain a single integer - the minimal score.


    Sample Input


    6
    10 1 50 50 20 5


    Sample Output

    3650

    md这道智障题充分证明我在区间dp上还有待修炼····注意要交这道题去zoj交,poj数据太tm水了···

    这道题其实很类似最开始讲义里提到的消除回文子串问题··甚至简单得多··也是一道区间消除问题···

    考虑到1,n是不能被消除的,我用f[i][j]表示消除i到j之间的数字最少需要多少分数(注意是之间),枚举中间点k,这里k可以看成最后一个消除的点,推出

    f[i][j]=max{f[i][j],f[i][k]+f[k][j]+num[k]*num[i]*num[j]}

    注意初始化时f[i][i]=f[i][i+1]=0,其他等于inf········
    代码:
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<ctime>
    #include<cctype>
    #include<cstring>
    #include<string>
    #include<algorithm>
    using namespace std;
    const int N=105;
    int n,num[N],f[N][N];
    const int inf=0x3f3f3f3f;
    inline int R()
    {
      char c;int f=0;
      for(c=getchar();c<'0'||c>'9';c=getchar());
      for(;c<='9'&&c>='0';c=getchar())
        f=(f<<3)+(f<<1)+c-'0';
      return f;
    }
    int main()
    {
      //freopen("a.in","r",stdin);
      while(scanf("%d",&n)!=EOF)
      {
        memset(f,inf,sizeof(f));
        for(int i=1;i<=n;i++)  num[i]=R(),f[i][i+1]=f[i][i]=0;  
        for(int i=n-2;i>=1;i--)
          for(int j=i+2;j<=n;j++)
            for(int k=i+1;k<j;k++) 
              f[i][j]=min(f[i][j],f[i][k]+f[k][j]+num[i]*num[k]*num[j]);
        cout<<f[1][n]<<endl;    
      }
      return 0;
    }

     5.Dire Wolf(hdu5115)

    Problem Description

    Dire wolves, also known as Dark wolves, are extraordinarily large and powerful wolves. Many, if not all, Dire Wolves appear to originate from Draenor.
    Dire wolves look like normal wolves, but these creatures are of nearly twice the size. These powerful beasts, 8 - 9 feet long and weighing 600 - 800 pounds, are the most well-known orc mounts. As tall as a man, these great wolves have long tusked jaws that look like they could snap an iron bar. They have burning red eyes. Dire wolves are mottled gray or black in color. Dire wolves thrive in the northern regions of Kalimdor and in Mulgore.
    Dire wolves are efficient pack hunters that kill anything they catch. They prefer to attack in packs, surrounding and flanking a foe when they can.
    — Wowpedia, Your wiki guide to the World of Warcra�

    Matt, an adventurer from the Eastern Kingdoms, meets a pack of dire wolves. There are N wolves standing in a row (numbered with 1 to N from left to right). Matt has to defeat all of them to survive.

    Once Matt defeats a dire wolf, he will take some damage which is equal to the wolf’s current attack. As gregarious beasts, each dire wolf i can increase its adjacent wolves’ attack by bi. Thus, each dire wolf i’s current attack consists of two parts, its basic attack ai and the extra attack provided by the current adjacent wolves. The increase of attack is temporary. Once a wolf is defeated, its adjacent wolves will no longer get extra attack from it. However, these two wolves (if exist) will become adjacent to each other now.

    For example, suppose there are 3 dire wolves standing in a row, whose basic attacks ai are (3, 5, 7), respectively. The extra attacks bi they can provide are (8, 2, 0). Thus, the current attacks of them are (5, 13, 9). If Matt defeats the second wolf first, he will get 13 points of damage and the alive wolves’ current attacks become (3, 15).

    As an alert and resourceful adventurer, Matt can decide the order of the dire wolves he defeats. Therefore, he wants to know the least damage he has to take to defeat all the wolves.

    Input

    The first line contains only one integer T , which indicates the number of test cases. For each test case, the first line contains only one integer N (2 ≤ N ≤ 200).

    The second line contains N integers ai (0 ≤ ai ≤ 100000), denoting the basic attack of each dire wolf.

    The third line contains N integers bi (0 ≤ bi ≤ 50000), denoting the extra attack each dire wolf can provide.

    Output

    For each test case, output a single line “Case #x: y”, where x is the case number (starting from 1), y is the least damage Matt needs to take.

    Sample Input

    2 3 3 5 7 8 2 0 10 1 3 5 7 9 2 4 6 8 10 9 4 1 2 1 2 1 4 5 1

    Sample Output

    Case #1: 17 Case #2: 74
    Hint
    In the first sample, Matt defeats the dire wolves from left to right. He takes 5 + 5 + 7 = 17 points of damage which is the least damage he has to take.
     
    又是一道区间消除问题,和上面一道题几乎一模一样···
    从例题4和5我也可以看出区间消除dp问题的规律·····考虑到每次枚举的状态互不影响,我们每次枚举左右区间l,r时,代表的都是消除l和r之间的序列,并不包含l和r,这样每次枚举中间点k,即最后一个消除的点时,转移方程就会简便很多······
    代码:
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<ctime>
    #include<cctype>
    #include<cstring>
    #include<string>
    #include<algorithm>
    using namespace std;
    const int N=205;
    const int inf=0x3f3f3f3f;
    int a[N],b[N],n,T,f[N][N];
    inline int R()
    {
      char c;int f=0;
      for(c=getchar();c<'0'||c>'9';c=getchar());
      for(;c<='9'&&c>='0';c=getchar())
        f=(f<<3)+(f<<1)+c-'0';
      return f;
    }
    inline int trans(int a)
    {
      if(a<0)  return 0;
      else return a;
    }
    int main()
    {
      //freopen("a.in","r",stdin);
      T=R();  
      for(int t=1;t<=T;t++)
      {
        memset(f,inf,sizeof(f));
        memset(b,0,sizeof(b));
        printf("Case #%d: ",t);
        n=R();
        for(int i=1;i<=n;i++)  a[i]=R();
        for(int i=1;i<=n;i++)  b[i]=R();
        for(int i=0;i<=n+1;i++)
          f[i][i]=f[i][i+1]=0;
        for(int i=n-1;i>=0;i--)
          for(int j=i+2;j<=n+1;j++)
            for(int k=i+1;k<j;k++)
              f[i][j]=min(f[i][j],f[i][k]+f[k][j]+b[i]+b[j]+a[k]);
        printf("%d
    ",f[0][n+1]); 
      }
      return 0;
    }

    6.Two Rabbits(hdu4745)

    Problem Description

    Long long ago, there lived two rabbits Tom and Jerry in the forest. On a sunny afternoon, they planned to play a game with some stones. There were n stones on the ground and they were arranged as a clockwise ring. That is to say, the first stone was adjacent to the second stone and the n-th stone, and the second stone is adjacent to the first stone and the third stone, and so on. The weight of the i-th stone is ai.

    The rabbits jumped from one stone to another. Tom always jumped clockwise, and Jerry always jumped anticlockwise.

    At the beginning, the rabbits both choose a stone and stand on it. Then at each turn, Tom should choose a stone which have not been stepped by itself and then jumped to it, and Jerry should do the same thing as Tom, but the jumping direction is anti-clockwise.

    For some unknown reason, at any time , the weight of the two stones on which the two rabbits stood should be equal. Besides, any rabbit couldn't jump over a stone which have been stepped by itself. In other words, if the Tom had stood on the second stone, it cannot jump from the first stone to the third stone or from the n-the stone to the 4-th stone.

    Please note that during the whole process, it was OK for the two rabbits to stand on a same stone at the same time. 

    Now they want to find out the maximum turns they can play if they follow the optimal strategy.

    Input

    The input contains at most 20 test cases.
    For each test cases, the first line contains a integer n denoting the number of stones.
    The next line contains n integers separated by space, and the i-th integer ai denotes the weight of the i-th stone.(1 <= n <= 1000, 1 <= ai <= 1000)
    The input ends with n = 0.

    Output

    For each test case, print a integer denoting the maximum turns.

    Sample Input

    1 1 4 1 1 2 1 6 2 1 1 2 1 3 0

    Sample Output

    1 4 5
    Hint
    For the second case, the path of the Tom is 1, 2, 3, 4, and the path of Jerry is 1, 4, 3, 2. For the third case, the path of Tom is 1,2,3,4,5 and the path of Jerry is 4,3,2,1,5.

    求环的最长不连续回文子串的模板题····详见http://blog.csdn.net/hcbbt/article/details/18949581

    代码:

    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<ctime>
    #include<cctype>
    #include<string>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    using namespace std;
    const int N=1005;
    int num[N],f[N][N],n;
    inline int R()
    {
      char c;int f=0;
      for(c=getchar();c<'0'||c>'9';c=getchar());
      for(;c<='9'&&c>='0';c=getchar())  f=(f<<3)+(f<<1)+c-'0';
      return f;
    }
    int main()
    {
      //freopen("a.in","r",stdin);
      while(true)  
      {
        n=R();if(!n)  break;
        memset(f,0,sizeof(f));
        for(int i=1;i<=n;i++) num[i]=R(),f[i][i]=1; 
        for(int l=1;l<n;l++)
          for(int i=1;i+l<=n;i++)  
          {        
            int j=l+i;
            f[i][j]=max(max(f[i+1][j],f[i][j-1]),f[i+1][j-1]+(num[i]==num[j]?2:0));
          }
        int ans=0;
        for(int i=1;i<=n;i++)  ans=max(ans,f[1][i]+f[i+1][n]);
        cout<<ans<<endl;
      }
      return 0;
    }
  • 相关阅读:
    Linux常用指令
    maven报错 java.lang.RuntimeException: com.google.inject.CreationException: Unable to create injector, see the following errors
    Idea 项目jdk环境配置
    Idea 设置maven配置文件settings.xml的位置
    IntelliJ IDEA常用快捷键总结
    Idea 一个窗口打开多个项目
    Git 下拉项目
    Git 删除本地保存的账号和密码
    mysql 查询奇偶数
    redis 短信验证码
  • 原文地址:https://www.cnblogs.com/AseanA/p/7553926.html
Copyright © 2011-2022 走看看