• dp训练


    1.

    假设你想以最美观的方式布置花店的橱窗。现在你有F束不同品种的花束,同时你也有至少同样数量的花瓶被按顺序摆成一行。这些花瓶的位置固定于架子上,并从1至V顺序编号,V是花瓶的数目,从左至右排列,则最左边的是花瓶1,最右边的是花瓶V。花束可以移动,并且每束花用1至F间的整数唯一标识。标识花束的整数决定了花束在花瓶中的顺序,如果I<J,则令花束I必须放在花束J左边的花瓶中。
      例如,假设一束杜鹃花的标识数为1,一束秋海棠的标识数为2,一束康乃馨的标识数为3,所有的花束在放入花瓶时必须保持其标识数的顺序,即:杜鹃花必须放在秋海棠左边的花瓶中,秋海棠必须放在康乃馨左边的花瓶中。如果花瓶的数目大于花束的数目。则多余的花瓶必须空置,且每个花瓶中只能放一束花。
      每一个花瓶都具有各自的特点。因此,当各个花瓶中放入不同的花束时,会产生不同的美学效果,并以美学值(一个整数)来表示,空置花瓶的美学值为零。
    在上述例子中,花瓶与花束的不同搭配所具有的美学值,如下表所示。

                                  花  瓶
                  1      2      3      4      5
         1 (杜鹃花)       7      23     -5       -24      16
         2 (秋海棠)       5      21     -4       10       23
         3 (康乃馨)      -21       5      -4       -20      20

         例如,根据上表,杜鹃花放在花瓶2中,会显得非常好看;但若放在花瓶4中则显得十分难看。
         为取得最佳美学效果,你必须在保持花束顺序的前提下,使花束的摆放取得最大的美学值。如果有不止一种的摆放方式具有最大的美学值,你只要输出字典序最小的那种摆放方式。

    训练第一题:

    重要在于输出方案,dp的时候顺便记录一下就好;

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 #include<cstring>
     5 using namespace std;
     6 const int maxn(105);
     7 #define LL long long
     8 #define up(i,j,n) for(int i=(j);(i)<=(n);i++)
     9 int n,m,f[maxn][maxn],pre[maxn][maxn],id[maxn][maxn],a[maxn][maxn],w[maxn];//a[i][j]表示第i花在j花瓶的价值
    10 int main(){
    11     freopen("dealing.in","r",stdin);
    12     freopen("dealing.out","w",stdout);
    13     memset(f,-10,sizeof(f));
    14     memset(pre,-10,sizeof(pre));
    15     scanf("%d%d",&n,&m);
    16     up(i,1,n)up(j,1,m)scanf("%d",&a[i][j]);
    17     up(i,0,m)f[0][i]=0;
    18     up(i,1,n)up(j,i,m)up(k,i-1,j-1){
    19         f[i][j]=max(f[i][j],f[i-1][k]+a[i][j]);
    20         if(pre[i][j]<f[i][j])pre[i][j]=f[i][j],id[i][j]=k;
    21     }
    22     int ans=-10000000;
    23     int t=m;
    24     up(i,n,m)if(ans<f[n][i])ans=f[n][i],t=i;
    25     printf("%d
    ",ans);
    26     w[n]=t;
    27     for(int i=n-1;i>=1;i--)
    28         w[i]=id[i+1][w[i+1]];
    29     for(int i=1;i<=n;i++)printf("%d ",w[i]);
    30     return 0; 
    31 }
    View Code

    2.

    在《Harry Potter and the Deathly Hallows》中,Harry Potter他们一起逃亡,现在有许多的东西要放到赫敏的包里面,但是包的大小有限,所以我们只能够在里面放入非常重要的物品,现在给出该种物品的数量、体积、价值的数值,希望你能够算出怎样能使背包的价值最大的组合方式,并且输出这个数值,赫敏会非常地感谢你。

    写得比较蠢,原因是自己把ij同时用了......

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 #include<cstring>
     5 using namespace std;
     6 const int maxn(5005);
     7 #define LL long long
     8 #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++)
     9 int w[maxn],v[maxn],cnt[maxn],n,m;
    10 int f[maxn];
    11 int main(){
    12     freopen("dealing.in","r",stdin);
    13     freopen("dealing.out","w",stdout);
    14     scanf("%d%d",&n,&m);
    15     up(i,1,n)scanf("%d%d%d",&cnt[i],&w[i],&v[i]);
    16     for(int i=1;i<=n;i++){
    17         if(cnt[i]*w[i]>=m)
    18             for(int j=w[i];j<=m;j++)f[j]=max(f[j],f[j-w[i]]+v[i]);
    19         else {
    20             int k=1;
    21             while(cnt[i]){
    22                 if(k>cnt[i])k=cnt[i],cnt[i]=0;
    23                 else cnt[i]-=k;
    24                 int W=w[i]*k,V=v[i]*k;
    25                 for(int j=m;j>=W;j--)f[j]=max(f[j],f[j-W]+V);
    26                 k<<=1;
    27             }
    28         }
    29     }
    30     printf("%d
    ",f[m]);
    31     return 0;
    32 }
    View Code

    3.

    在《Harry Potter and the Chamber of Secrets》中,Ron的魔杖因为坐他老爸的Flying Car撞到了打人柳,不幸被打断了,从此之后,他的魔杖的魔力就大大减少,甚至没办法执行他施的魔咒,这为Ron带来了不少的烦恼。这天上魔药课,Snape要他们每人配置一种魔药(不一定是一样的),Ron因为魔杖的问题,不能完成这个任务,他请Harry在魔药课上(自然是躲过了Snape的检查)帮他配置。现在Harry面前有两个坩埚,有许多种药材要放进坩埚里,但坩埚的能力有限,无法同时配置所有的药材。一个坩埚相同时间内只能加工一种药材,但是不一定每一种药材都要加进坩埚里。加工每种药材都有必须在一个起始时间和结束时间内完成(起始时间所在的那一刻和结束时间所在的那一刻也算在完成时间内),每种药材都有一个加工后的药效。现在要求的就是Harry可以得到最大的药效。

    这道题以前没真正理解,重写一遍,又有了新体会;

    这题首先对物品按结束时间排序,然后进行dp;

    为什么这么做;

    这题的难点之一在于无法对状态进行有效的转移;但是这么做使物品有序后,就能发现状态可以转移了;

    有时候排序对于优化dp是一种不错的方法;

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<algorithm>
     6 using namespace std;
     7 const int maxn(505);
     8 #define LL long long
     9 #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++)
    10 int f[maxn][maxn];
    11 int n,t;
    12 struct node{
    13     int x,y,v;
    14     bool operator<(const node& b)const{return y<b.y;}
    15     int operator-(int b)const{return x-b;}//这个是无聊写得......
    16 }a[maxn];
    17 int main(){
    18     freopen("dealing.in","r",stdin);
    19     freopen("dealing.out","w",stdout);
    20     scanf("%d%d",&t,&n);
    21     up(i,1,n)scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v);
    22     sort(a+1,a+n+1);
    23     for(int i=1;i<=n;i++){
    24         for(int j=t;j>=1;j--)
    25             for(int k=t;k>=0;k--){
    26                 if(j>=a[i].y)f[j][k]=max(f[j][k],f[a[i]-1][k]+a[i].v);
    27                 if(k>=a[i].y)f[j][k]=max(f[j][k],f[j][a[i]-1]+a[i].v);
    28             }
    29     }
    30     printf("%d
    ",f[t][t]);
    31     return 0;
    32 }
    View Code

    4.

    有两行自然数,UP[1..N],DOWN[1..M],如果UP[I]=DOWN[J]=K,那么上行的第I个位置的数就可以跟下行的第J个位置的数连一条线,称为一条K匹配,但是同一个位置的数最多只能连一条线。另外,每个K匹配都必须且至多跟一个L匹配相交且K≠L  。现在要求一个最大的匹配数。
    例如:以下两行数的最大匹配数为8
    ::点击图片在新窗口中打开::

    这题以前同样没怎么理解,于是再写一遍......

    设f[i][j]表示前i个a元素和前j个b元素一共能构成多少匹配;

    四重循环枚举,数据比较水,直接跑过了;

    5.

    帅帅经常更同学玩一个矩阵取数游戏:对于一个给定的n*m的矩阵,矩阵中的每个元素aij据为非负整数。游戏规则如下:
      1. 每次取数时须从每行各取走一个元素,共n个。m次后取完矩阵所有的元素;
      2. 每次取走的各个元素只能是该元素所在行的行首或行尾;
      3. 每次取数都有一个得分值,为每行取数的得分之和;每行取数的得分 = 被取走的元素值*2^i,其中i表示第i次取数(从1开始编号);
          4. 游戏结束总得分为m次取数得分之和。

      帅帅想请你帮忙写一个程序,对于任意矩阵,可以求出取数后的最大得分。

    原本想象成区间合并+特判,后来发现不对,每次只能在区间的两边增加数,这样才能保证合法;

    正序逆序都可以做,建议逆序写吧,好写;

    高精度,code比以前快了不少;

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstdlib>
      4 #include<cstring>
      5 #include<algorithm>
      6 using namespace std;
      7 const int maxn(105);
      8 #define LL long long
      9 #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++)
     10 int T,n;
     11 char s[maxn];
     12 
     13 struct bignum{
     14     
     15     int len,num[105];
     16     void print(){for(int i=len;i>=1;i--)cout<<num[i];cout<<endl;}
     17     void mem(){memset(num,0,sizeof(num));len=1;}
     18     bignum(){memset(num,0,sizeof(num));len=1;}
     19     bignum operator=(char* s){
     20         int n=strlen(s+1);
     21         memset(num,0,sizeof(num));
     22         for(int i=1;i<=n;i++)num[i]=s[n-i+1]-'0';
     23         len=n;
     24         return *this;
     25     }
     26     bignum operator=(int x){
     27         char s[maxn];
     28         sprintf(s+1,"%d",x);
     29         return *this=s;
     30     }
     31     friend bignum operator+(bignum a,bignum b){
     32         bignum c;
     33         int len=a.len;
     34         if(len<b.len)len=b.len;
     35         for(int i=1;i<=len;i++){
     36             c.num[i]+=a.num[i]+b.num[i];
     37             if(c.num[i]>=10)c.num[i]-=10,c.num[i+1]++;
     38         }
     39         while(c.num[len+1])len++;
     40         while(!c.num[len]&&len>1)len--;
     41         c.len=len;
     42         return c;
     43     }
     44     friend bignum operator*(bignum a,int x){
     45         bignum c;
     46         c.len=a.len;
     47         int len=a.len;
     48         for(int i=1;i<=len;i++){
     49             c.num[i]+=a.num[i]*x;
     50             int temp=i;
     51             while(c.num[temp]>=10)c.num[temp+1]+=c.num[temp]/10,c.num[temp]%=10,temp++;
     52             if(temp>len)len=temp;
     53         }
     54         while(!c.num[len]&&len>1)len--;
     55         while(c.num[len+1])len++;
     56         c.len=len;
     57         return c;
     58     }
     59     friend bignum operator*(bignum a,bignum b){
     60         bignum c;
     61         int len=a.len;
     62         if(len<b.len)len=b.len;
     63         for(int i=1;i<=a.len;i++)
     64             for(int j=1;j<=b.len;j++){
     65                 int k=i+j-1;
     66                 c.num[k]+=a.num[i]*b.num[j];
     67                 while(c.num[k]>=10)c.num[k+1]+=c.num[k]/10,c.num[k]%=10,k++;
     68                 len=k;
     69             }
     70         while(c.num[len]>=10)c.num[len+1]+=c.num[len]/10,c.num[len]%=10,len++;
     71         while(c.num[len+1])len++;
     72         while(!c.num[len]&&len>1)len--;
     73         c.len=len;
     74         return c;
     75     }
     76     bool operator<(const bignum &b)const{
     77         if(len<b.len)return 1;
     78         for(int i=len;i>=1;i--)if(num[i]!=b.num[i])return num[i]<b.num[i];
     79         return 0;
     80     }
     81 }a[maxn],ans,f[maxn][maxn],mi[maxn];
     82 int main(){
     83     freopen("dealing.in","r",stdin);
     84     freopen("dealing.out","w",stdout);
     85     scanf("%d%d",&T,&n);
     86     mi[0]=1;
     87     for(int i=1;i<=n;i++)mi[i]=mi[i-1]*2;
     88     while(T--){
     89         up(i,1,n){scanf("%s",s+1);a[i]=s;}
     90         up(i,1,n)f[i][i]=a[i];
     91         for(int p=2;p<=n;p++)
     92             for(int i=1;i+p-1<=n;i++){
     93                 int j=i+p-1;
     94                 f[i][j].mem();
     95                 f[i][j]=max(f[i+1][j]*2+a[i],f[i][j-1]*2+a[j]);
     96             }
     97         f[1][n]=f[1][n]*2;
     98         //f[1][n].print();
     99         ans=ans+f[1][n];
    100     }
    101     ans.print();
    102     return 0;
    103 }
    View Code

     5.

    小x在学习数列。他想到一个数学问题:
    现在有N个数的数列。现在你定义一个子序列是数列的连续一部分,子序列的值是这个子序列中最大值和最小值之差。
    给你这N个数,小x想知道所有子序列的值得累加和是多少。

    这题算是练习一下思维;

    首先要想到答案是所有最大值的和与所有最小值的和的差;

    用单调栈维护一下区间最小值和区间最大值,顺便处理一下它们的和;

     1 /*
     2 chad
     3 p1859
     4 2016/11/3
     5 */
     6 #include<iostream>
     7 #include<cstdio>
     8 #include<cstdlib>
     9 #include<cstring>
    10 #include<algorithm>
    11 using namespace std;
    12 const int maxn(303000);
    13 #define LL long long
    14 #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++)
    15 LL a[maxn],n;
    16 LL Min[maxn],Max[maxn],Min_=0,Max_=0;
    17 LL Suma,Sumb;
    18 LL ans=0;
    19 namespace IO{
    20     char buf[1<<15],*fs,*ft;
    21     int gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?0:*fs++;}
    22     int read(){
    23         int x=0,ch=gc(),f=0;
    24         while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();}
    25         while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();}
    26         return f?-x:x;
    27     }
    28 }using namespace IO;
    29 int main(){
    30     freopen("dealing.in","r",stdin);
    31     freopen("dealing.out","w",stdout);
    32     n=read();
    33     up(i,1,n)a[i]=read();
    34     up(i,1,n){
    35         while(Min_&&a[Min[Min_]]>a[i])Suma-=a[Min[Min_]]*(Min[Min_]-Min[Min_-1]),Min_--;
    36         Suma+=a[i]*(i-Min[Min_]);Min[++Min_]=i;
    37         while(Max_&&a[Max[Max_]]<a[i])Sumb-=a[Max[Max_]]*(Max[Max_]-Max[Max_-1]),Max_--;
    38         Sumb+=a[i]*(i-Max[Max_]);Max[++Max_]=i;
    39         ans+=Sumb-Suma;
    40     }
    41     cout<<ans<<endl;
    42     return 0;
    43 }
    View Code

    6.

    Goneril是一只非常缺觉的奶牛。她的一天被平均分割成N段(3 <= N <= 3,830),但是她要用其中的B段时间(2 <= B < N)睡觉。每段时间都有一个效用值U_i(0 <= U_i <= 200,000),只有当她睡觉的时候,才会发挥效用。
    有了闹钟的帮助,Goneril可以选择任意的时间入睡,当然,她只能在时间划分的边界处入睡、醒来。
    Goneril想使所有睡觉效用的总和最大。不幸的是,每一段睡眠的第一个时间阶段都是“入睡”阶段,而且不记入效用值。
    时间阶段是不断循环的圆(一天一天是循环的嘛),假如Goneril在时间N和时间1睡觉,那么她将得到时间1的效用值。

    环形dp是我深恶痛绝的类型之一;

    先照旧敲暴力,设f[i][j]表示前i个中选j个的最大得分,然后写了一个n^3的转移,最后发现自己貌似无法处理环形的问题,而且n^3也爆掉了;

    尽管过掉了大部分数据,但然并卵;

    想了一会儿,没法写了;

    题解挺神,设f[i][j][1/0]表示第i个节点选或不选,前面一共选j个的最大得分;

    这玩意是个n^2的转移方程;

    为什么可以做到n^2,因为在i点只有两种选择,取或不取,取的话也有两种选择,不取一种选择;我设置的状态不够简洁;

    这个状态也非常有利于我们下面的对环的处理;

    如果没有从n转到1的线段的话,普通的dp就可以解决;

    如果有从n转到1的线段,可以发现点1是一定要取的,必取无疑,就可以将f[i][1][1](i∈[2,n])全部设为-inf,这样在转移的时候,点1一定会成为第1个被选的的,这样就可以转移了;

     1 /*
     2 chad
     3 p1875
     4 2016/11/3
     5 */
     6 #include<iostream>
     7 #include<cstdio>
     8 #include<cstdlib>
     9 #include<cstring>
    10 #include<algorithm>
    11 using namespace std;
    12 const int maxn(3840),inf(100000000);
    13 #define LL long long
    14 #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++)
    15 namespace IO{
    16     char buf[1<<15],*fs,*ft;
    17     int gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?0:*fs++;}
    18     int read(){
    19         int x=0,ch=gc(),f=0;
    20         while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();}
    21         while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();}
    22         return f?-x:x;
    23     }
    24 }using namespace IO;
    25 int n,b;
    26 int a[maxn];
    27 int f[maxn][maxn][2];
    28 int main(){
    29     //freopen("dealing.in","r",stdin);
    30     //freopen("dealing.out","w",stdout);
    31     scanf("%d%d",&n,&b);
    32     up(i,1,n)a[i]=read();
    33     for(int i=2;i<=n;i++)
    34         for(int j=2;j<=b;j++){
    35             f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
    36             f[i][j][1]=max(f[i-1][j-1][1]+a[i],f[i-1][j-1][0]);
    37         }
    38     int ans=max(f[n][b][1],f[n][b][0]);
    39     memset(f,0,sizeof(f));
    40     for(int i=2;i<=n;i++)f[i][1][1]=-inf;
    41     for(int i=2;i<=n;i++)
    42         for(int j=2;j<=b;j++){
    43             f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
    44             f[i][j][1]=max(f[i-1][j-1][1]+a[i],f[i-1][j-1][0]);
    45         }
    46     ans=max(ans,f[n][b][1]+a[1]);
    47     printf("%d
    ",ans);
    48     return 0;
    49 }
    View Code

    7.

    某天,小x在玩一个经典小游戏——zumo。
    zumo游戏的规则是,给你一段长度为n的连续的彩色珠子,珠子的颜色不一定完全相同,但是,如果连续相同颜色的珠子大于等于k个,这些珠子就会消失。当然,最初的状态可能不必要直接消掉一些珠子(见样例)。现在你有无穷个所有颜色的珠子,并且你可以在任意地方插入珠子。
    现在提出问题:给你一个固定的状态,你最少需要用多少个小球,才能将所有的小球消去。

    分析了下题意,发现数列当前这样明显不好做,可以看出连续的颜色相同的合并比较好做,先合并一下;

    合并完,设f[i][j]表示把i到j部分的珠子消掉最少花费;

    然后想想怎么转移,然后敲了一会发现总是不过,就知道思路出问题了;

    看题解:这题解very  good!

    我想了想,f[i][j]这个状态的缺点在于转移的时候不好转移,因为如果要区间合并,我就需要知道区间的另外的一些信息,但我这个状态中找不到这个信息,于是我抓瞎了......

    这个故事告诉我们,一旦dp是发现状态没法转移了,信息不足,再多设一维状态,或者看看是否有多余状态,可以优化;

    dp这东西,一旦思路好了,走起来就很顺,所以发现此路不通就要及时调整思路;

    /*
    chad
    p1875
    2016/11/3
    */
    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn(105),inf(100000000);
    #define LL long long
    #define up(i,j,n) for(int i=(j);(i)<=(n);(i)++)
    namespace IO{
        char buf[1<<15],*fs,*ft;
        int gc(){return (fs==ft&&((ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft))?0:*fs++;}
        int read(){
            int x=0,ch=gc(),f=0;
            while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=gc();}
            while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=gc();}
            return f?-x:x;
        }
    }using namespace IO;
    int n,K;
    int a[maxn],b[maxn],top=0,v[maxn][maxn][2];
    int f[maxn][maxn][maxn];
    int w(int x){return K-x>0?K-x:0;}
    int dfs(int x,int y,int k){
        if(f[x][y][k]!=-1)return f[x][y][k];
        if(x==y)return f[x][y][k]=w(b[x]+k);
        f[x][y][k]=inf;
        f[x][y][k]=min(f[x][y][k],dfs(x,y-1,0)+w(b[y]+k));
        for(int i=x;i<y;i++)
            if(a[i]==a[y])f[x][y][k]=min(f[x][y][k],dfs(x,i,b[y]+k)+dfs(i+1,y-1,0));
        return f[x][y][k];
    }
    int main(){
        freopen("dealing.in","r",stdin);
        freopen("dealing.out","w",stdout);
        n=read(),K=read();
        up(i,1,n){
            int x=read();
            if(x==a[top])b[top]++;
            else a[++top]=x,b[top]=1;
        }
        n=top;
        memset(f,-1,sizeof(f));
        printf("%d
    ",dfs(1,n,0));
        return 0;
    }
    View Code

    8.

    小x,小y和小z在焦作一中东南角的臭水沟里发现了一堆宝藏。
    这堆宝藏是由n (1 <= n <= 20)包元宝组成,每包含si (1 <= si <= 100)个元宝,三个人很光明正大的决定把这些元宝私分。
    当然,他们都希望把这批宝藏尽可能的平分,他们又约定不能把包给拆开。这样,平分的标准就是获得元宝个数最多的那个人的元宝个数尽可能的小,他们约定,谁先算出,谁可以先挑。
    例如:现在有8包元宝,每包分别有元宝:2 4 5 8 9 14 15 20
    最公平的分法是:
    1: 2 9 15  一共获得 26
    2: 4 8 14  一共获得26
    3:  5 20    一共获得 25
    那么26就是我们需要的
    现在,眼红的你,能算出获得元宝最多的那个人最少能获得多少元宝么?

    见题目先敲贪心+dfs,水过;

    再想想dp怎么搞;

    设f[i][j][k]表示到第i个物品,一个人j元宝,一个人k元宝,另一个人可以算出来,这样可以暴力转移了......

    突然发现自己写的贪心+dfs有点小麻烦了;

    9.

     小x得到了一个珍贵的项链,这个项链由一圈N个透明的珠子组成。
    某天晚上,小x做了一个梦,这个透明的项链被染上了各种颜色,在醒来后,小x还清楚的记得项链的各个珠子颜色。而将项链染成梦中的颜色是小x最大的梦想。
    某位大神出现了,给了小x一个神奇的板刷,这个板刷可以将不超过K个连续的珠子刷成一个颜色,新刷的颜色会覆盖以前的颜色。而获得这个板刷的要求就是,大神要小x回答出最少用多少次板刷,可以完成小x的任务。
    比如小x的项链有5颗珠子,要染成的颜色分别是:2 1 2 1 2.刷子每次最多能刷3个珠子,可以先把编号为2 3 4的珠子 染成颜色1,编号为5 1的珠子染成颜色2,编号为3的珠子染成颜色2.最少用3次刷子就可以得到小x想要的颜色。
    现在小x把任务交给了要参加noi2013 的你,如果你能帮助他,你将获得通过noi2013所足够的rp。

    这题很不错(题面被老师修改,原题是sdut1309);

    我的思路是设个三维的状态f[i][j][w]表示当前ij区间所有的颜色是w,将这个区间的所有颜色刷成规定颜色的最小花费;

    状态转移方程:

    f[i][j]=f[i][j-1]  a[i]==a[j]&&j-i+1<=k   1    

    f[i][j]=min(f[i][p]+f[p+1][j])  a[i]==a[p]&&p-i+1<=k  //这个比较容易想到

    这题我最初想的时候,想的是在以最外面珠子的颜色的涂了一层后,需要记录一下当前珠子的颜色,但这么搞很明显不太科学,思路复杂;

    但不记录又很明显有后效性,因为在先涂了一种颜色后,里面的有些珠子已经满足了条件,不用再涂了,如果再涂一遍会挂;

    这时候的1式精妙之处就体现出来了,它并没有完全抛弃最外层,只是去掉了最外面的一个珠子,这样就很巧妙的避免了记录颜色这个状态,因为我们这样其实是默认了,最左端珠子的颜色是当前区间的颜色,然后在一步一步的递归中最后将刷的个数加上去,比三维的状态好的太多了;

    dp暂时告一段落;

  • 相关阅读:
    【SSRS】入门篇(六) -- 分组和总计
    【SSRS】入门篇(五) -- 设置报表格式
    【SSRS】入门篇(四) -- 向报表添加数据
    【SSRS】入门篇(三) -- 为报表定义数据集
    【SSRS】入门篇(二) -- 建立数据源
    【SSRS】入门篇(一) -- 创建SSRS项目
    【MS SQL】数据库维护计划之数据库备份(二)
    【MS SQL】数据库维护计划之数据库备份(一)
    【MS SQL】查看任务执行进度
    c++ 在客户端的GCC使用
  • 原文地址:https://www.cnblogs.com/chadinblog/p/6026571.html
Copyright © 2020-2023  润新知