动态规划初步习题(紫书)

不知道老师为什么突然要让我们写这个。。。。;


A  Longest Run on a Snowboard  (滑雪记忆搜)

dp[i][j]表示(i,j)位置的最长滑雪距离;

由于是严格小于的,所以不存在环的情况;

dp[i][j]=max{dp[i’][j’]+1}((i,j)和(i’,j’)相邻且a[i][j]<a[i’][j’]);

不太好确定顺序采用记忆化搜索的方式比较好些一点;

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 const int N=101;
 5 int n,m,mp[N][N],f[N][N];
 6 char s[N];
 7 int dx[4]={1,-1,0,0};
 8 int dy[4]={0,0,1,-1};
 9 int dp(int i,int j){
10     if(f[i][j])return f[i][j];
11     f[i][j]=1;
12     for(int k=0;k<4;k++){
13         int x=i+dx[k],y=j+dy[k];
14         if(x>=1&&x<=n&&y>=1&&y<=m&&mp[i][j]<mp[x][y])f[i][j]=max(f[i][j],dp(x,y)+1);
15     }
16     return f[i][j];
17 }
18 int main()
19 {    //freopen("A.in","r",stdin);
20     //freopen("A.out","w",stdout);
21     int T;
22     scanf("%d",&T);
23     while(T--){
24         scanf("%s%d%d",s,&n,&m);
25         for(int i=1;i<=n;i++)
26         for(int j=1;j<=m;j++)scanf("%d",&mp[i][j]),f[i][j]=0;
27         int ans=0;
28         for(int i=1;i<=n;i++)
29         for(int j=1;j<=m;j++)ans=max(ans,dp(i,j));
30         printf("%s: %d\n",s,ans);
31     }
32 }//by tkys_Austin; 
View Code

 


B  Free Candies               (看出结论n^4 dp)

其实没有什么特别的结论,只需要注意到当四个盒子里面拿的个数确定了,留在篮子里面的东西也确定了;

dp[i][j][k][l]表示四堆分别取到了i,j,k,l位置;

意思是我们可以知道仅看这个状态是否合法(留下的<=4),通过预处理递推可以知道这件事情;

考虑最后一次取的哪一堆,dp[i][j][k][l]从可行的上一个状态转移即可

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 const int N=50;
 5 int n,f[N][N][N][N],a[N][5],st[N][N][N][N],num[1<<20];
 6 int main()
 7 {    //freopen("B.in","r",stdin);
 8     //freopen("B.out","w",stdout);
 9     for(int i=1;i<=(1<<20)-1;i++)num[i]=num[i>>1]+(i&1); 
10     while(1){
11         scanf("%d",&n);
12         if(!n)break;
13         for(int i=1;i<=n;i++)
14         for(int j=1;j<=4;j++){
15             scanf("%d",&a[i][j]);
16         }
17         for(int i=0;i<=n;i++)
18         for(int j=0;j<=n;j++)
19         for(int k=0;k<=n;k++)
20         for(int l=0;l<=n;l++)
21         f[i][j][k][l]=st[i][j][k][l]=0;
22         f[0][0][0][0]=1;
23         int ans=0;
24         for(int i=0;i<=n;i++)
25         for(int j=0;j<=n;j++)
26         for(int k=0;k<=n;k++)
27         for(int l=0;l<=n;l++){
28             if(i)st[i][j][k][l]=st[i-1][j][k][l]^(1<<(a[i][1]-1)),f[i][j][k][l]|=f[i-1][j][k][l];
29             if(j)st[i][j][k][l]=st[i][j-1][k][l]^(1<<(a[j][2]-1)),f[i][j][k][l]|=f[i][j-1][k][l];
30             if(k)st[i][j][k][l]=st[i][j][k-1][l]^(1<<(a[k][3]-1)),f[i][j][k][l]|=f[i][j][k-1][l];
31             if(l)st[i][j][k][l]=st[i][j][k][l-1]^(1<<(a[l][4]-1)),f[i][j][k][l]|=f[i][j][k][l-1]; 
32             if(!f[i][j][k][l])continue;
33             if(num[st[i][j][k][l]]==5)f[i][j][k][l]=0;
34             ans=max(ans,(i+j+k+l-num[st[i][j][k][l]])>>1);
35         //    cout<<i<<" "<<j<<" "<<k<<" "<<l<<" "<<ans<<endl;
36         }
37         cout<<ans<<endl;
38     }
39     return 0;
40 }//by tkys_Austin; 
View Code

 


C  Cake slicing               (经典的区间dp)

dp[i][j][k][l]表示从(k,l)到(i,j)之间的矩形部分合法切割的最小长度;(k<i,l<j)

转移的话只需要枚举一刀,枚举横着切还是竖着切和切哪个位置,设动刀的地方是x和x+1之间:

切行:dp[i][j][k][l]=min{dp[i][j][k][l],dp[i][j][x+1][l]+dp[x][j][k][l]+(j-l+1)};

切列:dp[i][j][k][l]=min{dp[i][j][k][l],dp[i][j][k][x+1]+dp[i][x][k][l]+(i-k+1)};

特别强调一下初始化:如果(i,j,k,l)的矩形内有且仅有一个标记点才为0,其余inf;

 1 #include<cstdio>
 2 #include<iostream>
 3 #define FOR for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)for(int i1=1;i1<=i;i1++)for(int j1=1;j1<=j;j1++)
 4 using namespace std;
 5 const int N=21;
 6 int n,m,k,sum[N][N],f[N][N][N][N],inf=0x3f3f3f3f;//
 7 void Min(int&x,int y){if(x>y)x=y;}//
 8 int main()
 9 {    //freopen("C.in","r",stdin);
10     //freopen("C.out","w",stdout);
11     int T=0;
12     while(scanf("%d%d%d",&n,&m,&k)!=EOF){//
13         for(int i=1;i<=n;i++)
14         for(int j=1;j<=m;j++)sum[i][j]=0;//
15         for(int i=1,x,y;i<=k;i++)scanf("%d%d",&x,&y),sum[x][y]=1;//
16         for(int i=1;i<=n;i++)
17         for(int j=1;j<=m;j++)sum[i][j]+=sum[i][j-1];//
18         for(int j=1;j<=m;j++)
19         for(int i=1;i<=n;i++)sum[i][j]+=sum[i-1][j];//
20         FOR if(sum[i][j]-sum[i][j1-1]-sum[i1-1][j]+sum[i1-1][j1-1]==1)f[i][j][i1][j1]=0;else f[i][j][i1][j1]=inf;//
21         for(int leni=1;leni<=n;leni++)
22         for(int lenj=1;lenj<=m;lenj++)
23         for(int i1=1;i1+leni-1<=n;i1++)
24         for(int j1=1;j1+lenj-1<=m;j1++)
25         {    int i=i1+leni-1,j=j1+lenj-1;
26             for(int ki=i1;ki<i;ki++)Min(f[i][j][i1][j1],f[i][j][ki+1][j1]+f[ki][j][i1][j1]+j-j1+1);//
27             for(int kj=j1;kj<j;kj++)Min(f[i][j][i1][j1],f[i][j][i1][kj+1]+f[i][kj][i1][j1]+i-i1+1);//
28         }//
29         printf("Case %d: %d\n",++T,f[n][m][1][1]);//
30     }
31     return 0;
32 }//by tkys_Austin; 
View Code

 

 


D  Folding                    (区间dp,输出方案比较恶心)

dp[i][j]表示区间s[i]到s[j]的子串的最小长度(我就懒得写转移方程了)

要么自己折叠成自己的 K(循环节),要么分成两个区间的折叠;

但是注意循环节可能已经被折叠了,所以还是应该调用其dp值;

输出方案可以用string边转移边存,也可以像我一样沙茶地转移完再往回找;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=200,inf=0x3f3f3f3f;
 6 int n,fd[N][N],dp[N][N],len[N][N],val[N];//
 7 char s[N],ans[N],T[N];//
 8 void find_ans(int l,int r,int st){
 9     if(dp[l][r]==len[l][r]){
10         for(int i=1;i<=dp[l][r];i++)ans[st+i]=s[l+i-1];
11         return ;
12     }//
13     if(dp[l][r]==val[len[l][r]/fd[l][r]]+dp[l][l+fd[l][r]-1]+2){
14         int tmp=len[l][r]/fd[l][r];
15         int pre=tmp;
16         for(int i=1;i<=val[pre];i++){T[i]=tmp%10+'0',tmp/=10;}
17         tmp=pre;
18         for(int i=1;i<=val[tmp]>>1;i++)swap(T[i],T[val[tmp]-i+1]);
19         for(int i=1;i<=val[tmp];i++)ans[++st]=T[i];
20         
21         ans[++st]='(',ans[st+dp[l][l+fd[l][r]-1]+1]=')';
22         find_ans(l,l+fd[l][r]-1,st);
23         return ;
24     }//
25     for(int k=l;k<r;k++)if(dp[l][r]==dp[l][k]+dp[k+1][r]){
26         find_ans(l,k,st);find_ans(k+1,r,st+dp[l][k]);    
27         return ;
28     }//
29 }//
30 int main()
31 {    //freopen("D.in","r",stdin);
32     //freopen("D.out","w",stdout);
33     for(int i=1;i<=100;i++)val[i]=val[i/10]+1;
34     while(scanf("%s",s+1)!=EOF){
35         n=strlen(s+1); 
36         for(int i=1;i<=n;i++)
37         for(int j=1;j<=n;j++)dp[i][j]=len[i][j]=max(j-i+1,0);//
38         for(int i=1;i<=n;i++)
39         for(int j=i;j<=n;j++)
40         for(int k=len[i][j];k>=1;k--)if(!(len[i][j]%k)){
41             int fg=1;
42             for(int l=0;l<len[i][j];l++)if(s[i+l]!=s[i+(l%k)]){fg=0;break;}
43             if(fg)fd[i][j]=k;
44         }//
45         for(int L=1;L<=n;L++)
46         for(int i=1;i+L-1<=n;i++){
47             int j=i+L-1;
48             dp[i][j]=min(dp[i][j],val[len[i][j]/fd[i][j]]+dp[i][i+fd[i][j]-1]+2);
49             for(int k=i;k<j;k++){dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]);}
50         }//
51         find_ans(1,n,0);
52         for(int i=1;i<=dp[1][n];i++){
53             printf("%c",ans[i]);
54         }//
55         printf("\n");
56     }//
57     return 0;
58 }//by tkys_Austin;
View Code

 

 


E  Stamps and Envelope Size   (类似于01背包)

不就是个问可行性的背包吗。。。。。。。

注意判断题中的条件就好;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 int n,s,f[1010][11];
 7 struct data{
 8     int m,a[11],ans;
 9     bool operator<(const data&A)const{
10         if(ans!=A.ans)return ans>A.ans; //
11         if(m!=A.m)return m<A.m;//
12         for(int i=m;i>=1;i--)if(a[i]!=A.a[i])return a[i]<A.a[i];//
13         return true;
14     }//
15 }q[11];//
16 void dp(int x){
17     memset(f,0,sizeof(f));
18     f[0][0]=1;
19     for(int i=1;i<=1000;i++)
20     for(int j=1;j<=s;j++)
21     for(int k=1;k<=q[x].m;k++){
22         if(q[x].a[k]<=i)f[i][j]|=f[i-q[x].a[k]][j-1];
23     }
24 }//
25 int main()
26 {    //freopen("E.in","r",stdin);
27     //freopen("E.out","w",stdout);
28     while(1){
29         scanf("%d",&s);
30         if(!s)break;        
31         scanf("%d",&n);
32         for(int i=1;i<=n;i++){
33             scanf("%d",&q[i].m);
34             for(int j=1;j<=q[i].m;j++)scanf("%d",&q[i].a[j]);
35             q[i].ans=0;dp(i);
36             for(int j=1;j<=1000;j++){
37             int fg=0;for(int k=1;k<=s;k++)if(f[j][k])fg=1;
38             if(!fg){q[i].ans=j-1;break;}
39             }
40         }//
41         sort(q+1,q+n+1);
42         printf("max coverage = %3d :",q[1].ans);
43         for(int i=1;i<=q[1].m;i++)printf(" %2d",q[1].a[i]);
44         printf("\n");//
45     }
46     return 0;
47 }//by tkys_Austin; 
View Code

 


F  Cyborg Genes               (和LCS有点关系,状态题)

题意是求C串使得A,B是其子串,C的最小长度和方案数;

网上有LCS相关的解法好像;(最小长度==len(A)+len(B)-LCS(A,B));

但我是:dp[i][j][k]表示构造到第i为,和A比对到i为,B到j位;

枚举下一个数字相同就j或k  +1,转移方案;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=50;
 6 typedef int ll;
 7 int n;
 8 ll dp[N<<1][N][N];
 9 char s1[N],s2[N];
10 int main()
11 {    //freopen("F.in","r",stdin);
12     //freopen("F.out","w",stdout);
13     scanf("%d%*c",&n);
14     for(int Case=1;Case<=n;Case++){
15         gets(s1+1),gets(s2+1);
16         int l1=strlen(s1+1),l2=strlen(s2+1);
17         memset(dp,0,sizeof(dp));
18         dp[0][0][0]=1;
19         int ans1=0; ll ans2=0;
20         for(int i=0;i<=l1+l2;i++){
21             for(int j=0;j<=l1;j++)
22             for(int k=0;k<=l2;k++)if(dp[i][j][k])
23             for(int ch='A';ch<='X';ch++)
24             dp[i+1][min(l1,j+(s1[j+1]==ch))][min(l2,k+(s2[k+1]==ch))]+=dp[i][j][k]; 
25             if(dp[i][l1][l2]){ans1=i;ans2=dp[i][l1][l2];break;} 
26         }
27         printf("Case #%d: %d %d\n",Case,ans1,ans2);
28     }
29     return 0;
30 }//by tkys_Austin;
View Code

 

 


G  Locker                     (学会设计状态好的,针对性转移)

假设我们1-i一位一位地转,i有可能会影响i+1,和i+2;

dp[i][j][k]表示转到第i位,前i位已经转好,i+1位为j,i+2位为k,我们可以顺时针和逆时针转;(另次数为c1,c2,c3)

可以转i+1,i+1和i+2,i+1和i+2和i+3,由于必须要转回第i位到锁,所以转的次数d已经确定了;

下一个状态为f[i+1][k+c1][a[i+3]+c2]+d    (具体还有些细节看代码)

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=1010;
 6 int n,a[N],b[N],f[N][10][10];
 7 char s1[N],s2[N];
 8 void Min(int&x,int y){if(x>y)x=y;}
 9 int main()
10 {    //freopen("G.in","r",stdin);
11     //freopen("G.out","w",stdout);
12     while(scanf("%s%s",s1+1,s2+1)!=EOF){
13         n=strlen(s1+1);
14         for(int i=1;i<=n;i++)a[i]=s1[i]-'0',b[i]=s2[i]-'0';
15         a[n+1]=a[n+2]=b[n+1]=b[n+2]=0;
16         memset(f,0x3f,sizeof(f));
17         f[0][a[1]][a[2]]=0;//
18         for(int i=0;i<n;i++)
19         for(int j=0;j<=9;j++)
20         for(int k=0;k<=9;k++){
21             int d=(b[i+1]-j+10)%10;//
22             for(int j1=0;j1<=d;j1++)
23             for(int k1=0;k1<=j1;k1++)
24                 Min(f[i+1][(k+j1)%10][(a[i+3]+k1)%10],f[i][j][k]+d);//
25             d=(j-b[i+1]+10)%10;//
26             for(int j1=0;j1<=d;j1++)
27             for(int k1=0;k1<=j1;k1++)
28                 Min(f[i+1][(k-j1+10)%10][(a[i+3]-k1+10)%10],f[i][j][k]+d);//
29         }//
30         cout<<f[n][0][0]<<endl;//
31     }
32     return 0;
33 }//by tkys_Austin;
View Code

 

 


H  Alibaba                    (区间dp,和关灯类似)

由贪心可知,我们每一次已经访问的点一定是一个连续的区间[l,r]

dp[l][r][0/1]表示我们当前访问的区间和和现在l还是在r;

枚举下一次取l-1,还是r+1,由于保存了01起点和终点都是有的,算出时间<=d[i]才转移;

 1 #include<cstdio>
 2 #include<iostream>
 3 typedef int ll;
 4 using namespace std;
 5 const int N=10010;
 6 const ll inf=0x3f3f3f3f;
 7 int n,d[N],t[N];
 8 ll f[N][N][2];
 9 void Min(ll&x,ll y){if(x>y)x=y;}
10 int main()
11 {    //freopen("H.in","r",stdin);
12     //freopen("H.out","w",stdout);
13     while(scanf("%d",&n)!=EOF){
14         for(int i=1;i<=n;i++)scanf("%d%d",&d[i],&t[i]),f[i][i][0]=f[i][i][1]=0;//
15         for(int len=2;len<=n;len++)
16         for(int i=1;i+len-1<=n;i++){
17             int j=i+len-1;ll tmp;
18             f[i][j][0]=f[i][j][1]=inf;
19             if((tmp=f[i+1][j][0]+d[i+1]-d[i])<t[i])Min(f[i][j][0],tmp);
20             if((tmp=f[i+1][j][1]+d[j]-d[i])<t[i])Min(f[i][j][0],tmp);
21             if((tmp=f[i][j-1][1]+d[j]-d[j-1])<t[j])Min(f[i][j][1],tmp);
22             if((tmp=f[i][j-1][0]+d[j]-d[i])<t[j])Min(f[i][j][1],tmp);
23         }//
24         ll ans=min(f[1][n][0],f[1][n][1]);
25         if(ans==inf)puts("No solution");
26         else cout<<ans<<endl;//
27     }
28     return 0;
29 }//by tkys_Austin;
View Code

 

 


I  Storage Keeper             (两次dp)

先考虑最大安全值:dp[i][j]表示选到前j个守卫看守前i个谷仓的最大值;

枚举j号守卫看守了哪些谷仓;

第二次f[i][j]定义同样表示最小价值;

那可不可以写到一起呢?不可以因为f[i][j]转移不一定要每次安全值都最大,只要大于等于dp[n][m]答案的安全值就好;

像我就先写了一个结构体一起dp然后疯狂wa;。。。

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 const int N=101,inf=0x3f3f3f3f;
 5 int n,m,a[N],f[N][N],c[N][N];//
 6 int main()
 7 {    //freopen("I.in","r",stdin);
 8     //freopen("I.out","w",stdout);
 9     while(1){
10         scanf("%d%d",&n,&m);
11         if(!n&&!m)break;
12         for(int i=1;i<=m;i++)scanf("%d",&a[i]);//
13         for(int i=1;i<=n;i++)f[i][0]=0,c[i][0]=inf;
14         for(int i=0;i<=m;i++)f[0][i]=inf,c[0][i]=0;//
15         for(int i=1;i<=n;i++)
16         for(int j=1;j<=m;j++){
17             f[i][j]=f[i][j-1];
18             for(int k=1;k<=i;k++)
19             f[i][j]=max(min(f[i-k][j-1],a[j]/k),f[i][j]);
20         }///
21         if(!f[n][m]){
22             cout<<0<<" "<<0<<endl;
23             continue;
24         }///
25         for(int i=1;i<=n;i++)
26         for(int j=1;j<=m;j++){
27             c[i][j]=c[i][j-1];
28             for(int k=1;/*a[j]/k>=f[n][m]*/k<=min(i,a[j]/f[n][m]);k++)c[i][j]=min(c[i][j],c[i-k][j-1]+a[j]);
29         }///
30         cout<<f[n][m]<<" "<<c[n][m]<<endl;
31     }//
32     return 0;
33 }//by tkys_Austin;
View Code

 

 

下一个之前先推荐一个番外:
基于贪心的几类区间覆盖问题:
https://www.cnblogs.com/Draymonder/p/7215230.html 


J  Barisal Stadium            (计算几何+DP)

预处理一个点可以看到的多边形的边的范围:

枚举点v,枚举多边形上面的点p[i],p[i+1],如果向量p[i+1]-v在p[i]-v的右边则看的到,反之则看不到(包括重合);

然后可以枚举起点,暴力写一个n^3的带权线段覆盖的dp;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 using namespace std;
 5 const int N=1010,inf=0x3f3f3f3f;
 6 int n,m,f[N],fg[N];//
 7 struct point{
 8     double x,y;int w;
 9     point(double X=0,double Y=0):x(X),y(Y){};
10     point operator -(const point&A){return point(x-A.x,y-A.y);}//
11 }p[N],v[N];
12 struct query{int x,y;int w;}q[N];
13 double cross(point A,point B){return A.x*B.y-A.y*B.x;}//
14 void cal(int id,point A){
15     for(int i=1;i<=n;i++){
16         fg[i]= (bool)(cross(p[i+1]-A,p[i]-A)>0) ;
17     }
18     if(fg[1]&&fg[n]){
19         int l=1,r=n;
20         while(fg[l+1])l++;
21         while(fg[r-1])r--;
22         q[id].x=r,q[id].y=l+n;
23     }
24     else {
25         int l=1,r=n;
26         while(!fg[l])l++;
27         while(!fg[r])r--;
28         q[id].x=l,q[id].y=r;
29     }
30 }///
31 int dp(int S){
32     for(int i=S;i<=S+n;i++)f[i]=inf;//
33     f[S]=0;//
34     for(int i=S;i<=S+n-1;i++)
35     for(int j=1;j<=m;j++){
36         if(q[j].x>i)continue;
37         int tmp=min(S+n,q[j].y+1);
38         f[tmp]=min(f[tmp],f[i]+q[j].w);
39     }//
40     return f[S+n];
41 } //
42 int main()
43 {    //freopen("J.in","r",stdin);
44     //freopen("J.out","w",stdout);
45     while(1){
46         scanf("%d",&n);if(!n)break;
47         for(int i=1;i<=n;i++)scanf("%lf%lf",&p[i].x,&p[i].y);p[n+1]=p[1];
48         scanf("%d",&m);
49         for(int i=1;i<=m;i++)scanf("%lf%lf%d",&v[i].x,&v[i].y,&q[i].w);
50         for(int i=1;i<=m;i++)cal(i,v[i]);
51         int ans=inf;
52         for(int i=1;i<=n;i++)ans=min(ans,dp(i));
53         if(ans==inf)cout<<"Impossible."<<endl;
54         else cout<<ans<<endl;
55     }///
56     return 0;
57 }//by tkys_Austin;
View Code

 

 

 

K  Dyslexic Gollum            (壮压dp,注意判断len+1)

壮压末尾k(<=10)位的状态,dp[i][S]表示到第i为末尾为S;

预处理S是不是回文,限制转移;

但是可能在末尾的len+1,为不经过len长度的回文直接形成了,转移的时候再判断一下;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 int mod=1e9+7;
 6 const int N=410;
 7 int T,n,k,f[N][(1<<10)],vis1[(1<<10)],vis2[(1<<11)];
 8 void pre(int* Vis,int len){
 9     for(int S=0;S<(1<<len);S++){
10         int S1=0;
11         for(int i=1;i<=len;i++)
12         S1=(S1<<1)|((S>>(i-1))&1);
13         if(!(S^S1))Vis[S]=1;else Vis[S]=0;
14     }
15 }
16 void Upd(int&x,int y){x=(x+y)%mod;}
17 int main()
18 {    //freopen("K.in","r",stdin);
19     //freopen("K.out","w",stdout);
20     scanf("%d",&T);
21     while(T--){
22         scanf("%d%d",&n,&k);
23         pre(vis1,k);pre(vis2,k+1);
24         if(n<k){cout<<(1<<n)<<endl;continue;}//
25         memset(f,0,sizeof(f));
26         for(int S=0;S<(1<<k);S++)f[k][S]=1;//
27         for(int i=k;i<n;i++)
28         for(int S=0;S<(1<<k);S++){
29             if(vis1[S])continue;
30             int tmp=(S<<1)&((1<<k)-1);
31             if(!vis2[(S<<1)])Upd(f[i+1][tmp],f[i][S]);
32             if(!vis2[(S<<1)^1])Upd(f[i+1][tmp^1],f[i][S]);
33         }///
34         int ans=0;
35         for(int S=0;S<(1<<k);S++)
36         if(!vis1[S])Upd(ans,f[n][S]);
37         cout<<ans<<endl;
38     }//
39     return 0;
40 }//by tkys_Austin;
View Code

 

 

 


L  Protecting Zonk            (树形dp,树边覆盖的扩展,状态设计)

dp[i][k](k=0,1,2)表示i和i的子树被看守,同时从i节点还可以向上看守的最大距离;

k=0,i必不选,son[i]必选C1;

k=1,i不选son[i]必至少有一个为C2,其余随便;i选C1,son[i]随便;

k=2;i必选,son[i]随便,甚至son[son[i]]都可以随便,

  再记录一个g[i]表示i不必被看守但儿子都被看守的最小值,k=2这种情况g转移过去;

  g的转移son[i]随便选;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=10011,inf=0x3f3f3f3f;//
 6 int n,C1,C2,f[N][3],hd[N],o,g[N];//
 7 struct Edge{int v,nt;}E[N<<1];//
 8 void adde(int u,int v){
 9     E[o]=(Edge){v,hd[u]}; hd[u]=o++;
10     E[o]=(Edge){u,hd[v]}; hd[v]=o++;
11 }//
12 void dp(int u,int fa){
13     int mn=inf;
14     f[u][0]=0;f[u][1]=C1;f[u][2]=C2;g[u]=0; 
15     for(int i=hd[u];~i;i=E[i].nt){
16         int v=E[i].v;
17         if(v==fa)continue;
18         dp(v,u); 
19         int t=min(f[v][0],min(f[v][1],f[v][2]));
20         f[u][0]+=f[v][1];
21         f[u][1]+=t;mn=min(mn,f[v][2]-t);
22         f[u][2]+=min(t,g[v]);
23         g[u]+=t;
24     }
25     f[u][1]=min(f[u][1],g[u]+mn);
26 }//
27 int main()
28 {    //freopen("L.in","r",stdin);
29     //freopen("L.out","w",stdout);
30     while(scanf("%d%d%d",&n,&C1,&C2)!=EOF){
31         if(!n&&!C1&&!C2)break;
32         o=0;for(int i=1;i<=n;i++)hd[i]=-1;
33         for(int i=1,u,v;i<n;i++)scanf("%d%d",&u,&v),adde(u,v);//
34         dp(1,0);//
35         int ans=min(f[1][0],min(f[1][1],f[1][2]));//
36         cout<<ans<<endl;
37     }///
38     return 0;
39 }//by tkys_Austin;
View Code

 

 


M  Stacking Plates            (贪心+dp,考虑全面哦!)

先分开再合并,能不分开尽量不分开;

拍完序后考虑最小的不分开的堆数;

即连续的本来就在一个集合里面的可以不分开;

把排序后的盘子按大小值分成若干新堆,值相同的分在一堆;

值相同原来又属于同一个堆的一定不分开,可以离散化;

dp[i][j]表示新的i堆已经和前面的i-1堆合并了j编号的盘子,那么这次j不用就好;

用离散化的tot-dp[tot][]就是最小堆数;

最小堆数*2-n-1就是答案;

(说的不清楚,具体看代码)

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 using namespace std;
 6 const int N=100;
 7 int n,m,tot,f[N*N][N],mp[N*N][N],sz[N*N];
 8 struct data{
 9     int x,id;
10     bool operator <(const data&A)const{return (x==A.x)?id<A.id:x<A.x;}//
11     bool operator ==(const data&A)const{return (x==A.x)&&(id==A.id);}//
12 }a[N*N];//
13 int main()
14 {    //freopen("M.in","r",stdin);
15     //freopen("M.out","w",stdout); 
16     int Case=0;
17     while(scanf("%d",&n)!=EOF){
18         tot=0;
19         for(int i=1;i<=n;i++){
20             scanf("%d",&m);
21             for(int j=1;j<=m;j++)scanf("%d",&a[++tot].x),a[tot].id=i;
22         }//
23         sort(a+1,a+tot+1);//
24         tot=unique(a+1,a+tot+1)-a-1;
25         int sum=0;
26         for(int i=1;i<=tot;i++){
27             if(a[i].x!=a[i-1].x)memset(mp[++sum],0,sizeof(mp[sum])),sz[sum]=0;
28             mp[sum][a[i].id]=1,sz[sum]++;
29         }//
30         for(int i=1;i<=n+1;i++)f[1][i]=0;//
31         for(int i=2;i<=sum;i++)
32         for(int j=1;j<=n+1;j++){
33             f[i][j]=0;
34             for(int k=1;k<=n+1;k++){
35                 f[i][j]=max(f[i][j],f[i-1][k]);
36                 if((j!=k||sz[i]==1)&&mp[i-1][k]&&mp[i][k])f[i][j]=max(f[i][j],f[i-1][k]+1);
37             }
38         }//
39         int ans=((tot-f[sum][n+1])<<1)-n-1;
40         printf("Case %d: %d\n",++Case,ans);
41     }
42     return 0;
43 }//by tkys_Austin;
View Code

 

 


N  Telescope                  (计算几何+环上k限制最短回路)

在圆上选m个点使得m多边形面积最大;

加入选了i紧接着选了j,那么就从整个的圆里面减去形成的弓形面积;

意思是i可以向j连边代价是弓形面积,就成了在环上跑m个节点的最短环路;

dp[i][k]表示选到i号节点一共k个节点的最短;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 using namespace std;
 6 typedef long double LD;
 7 const int N=101;
 8 const LD pi = 3.1415926535;
 9 int n,m;
10 LD p[N],C[N][N],SC,dp[N][N];
11 int main()
12 {    //freopen("N.in","r",stdin);
13     //freopen("N.out","w",stdout);
14     while(scanf("%d%d",&n,&m)!=EOF&&n){
15         for(int i=1;i<=n;i++)scanf("%Lf",&p[i]);
16         sort(p+1,p+n+1);
17         for(int i=1;i<=n;i++)p[i+n]=p[i]+1;
18         for(int i=1;i<=(n<<1);i++)
19         for(int j=i+1;j<=(n<<1);j++)
20         C[i][j]=pi*(p[j]-p[i])-sin(2*pi*(p[j]-p[i]))/2;
21         LD ans=pi;
22         for(int S=2;S<=n;S++){
23             for(int i=S;i<=S+n;i++)for(int j=0;j<=m;j++)dp[i][j]=pi;
24             dp[S][0]=0;
25             for(int i=S;i<=S+n-1;i++)
26             for(int j=0;j<m;j++){
27                 for(int k=i+1;k<=S+n;k++){
28                     dp[k][j+1]=min(dp[k][j+1],dp[i][j]+C[i][k]);
29                 }
30             }
31             ans=min(ans,dp[S+n][m]);
32         }
33         printf("%.6Lf\n",pi-ans);
34     }
35     return 0;
36 }//by tkys_Austin; 
View Code

 

 


O  Learning Vector            (贪心+k限制背包(你想的话可以wqs二分)

简化问题:如果只是n个向量首尾相接和x轴围成的最大面积;

那么直接按照斜率大到小排序即可;(考虑交换不符合的相邻两个可以证明)

先排序就变成了简单的选k个的问题;

dp[i][j][k]表示前i个向量选j个,k为此时的y坐标,这样就可以转移面积;

本来感觉应该过不了但是UVa的机子之强悍

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=51,inf=0x3f3f3f3f;
 6 int T,n,m,f[N][N*N];
 7 struct point{
 8     int x,y;
 9     bool operator <(const point&A)const{return y*A.x>A.y*x;}
10 }p[N];
11 int main()
12 {    //freopen("O.in","r",stdin);
13     //freopen("O.out","w",stdout);
14     scanf("%d",&T);
15     int Case=0;
16     while(T--){
17         int Maxy=0;
18         scanf("%d%d",&n,&m);
19         for(int i=1;i<=n;i++)scanf("%d%d",&p[i].x,&p[i].y),Maxy+=p[i].y;
20         sort(p+1,p+n+1);
21         for(int i=0;i<=n;i++)
22         for(int j=0;j<=Maxy;j++)f[i][j]=-inf;
23         f[0][0]=0;
24         int ans=0;
25         for(int i=1;i<=n;i++)
26         for(int j=m;j>=1;j--)
27         for(int k=Maxy;k>=1;k--){
28             if(p[i].y<=k)f[j][k]=max(f[j][k],f[j-1][k-p[i].y]+((k<<1)-p[i].y)*p[i].x);
29             if(j==m)ans=max(ans,f[j][k]);
30         }
31         printf("Case %d: %d\n",++Case,ans);
32     }
33     return 0;
34 }//by tkys_Austin;
View Code

 

有兴趣的可以看下wqs二分,没有试过但应该可以用;

https://blog.csdn.net/chhnz/article/details/78827430


P  The Picnic                 (转化答案+枚举+dp)

题目的要求是:一个面积最大的凸多边形;

枚举多边形最左下边的点,考虑如何构造一个凸多边形;

可以用很多个有一个公共顶点的三角形构成多边形,只需保证它是凸的;

先将原来的点排序,依次选取作为左下的点p[i]作为起点,将p[j]-p[i](j>i)加入新数组q;

将这些向量按斜率排序,问题变成选这些向量且凸的最大面积;

dp[J][I](J<I)表示最后选的q[I]和q[J],枚举q[K],如果K,J,I满足凸包性质转移;

初始化的时候如果J,I里面有其它向量就inf;

注意初始化时如果K和J,或I斜率相同不能inf,因为此时是可能可以作为开头(J,K相同)或结尾(I,K)相同的;

q一开始就按照第一斜率第二长度(优先大)排序,转移如果slope(J-1)==slope(J)就break;掉,对相同时最短的也没有影响;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm> 
 5 using namespace std;
 6 const int N=101,inf=0x3f3f3f3f;
 7 int n,m;
 8 struct point{
 9     int x,y;
10     point(int X=0,int Y=0):x(X),y(Y){};
11     bool operator <(const point&A)const{return (x==A.x)?y<A.y:x<A.x;}
12     point operator -(const point&A){return point(x-A.x,y-A.y);}
13 }p[N],q[N];//
14 int f[N][N],vis[N][N];
15 int len(point A){return (A.x*A.x+A.y*A.y);}//
16 int cross(point A,point B){return A.x*B.y-A.y*B.x;}//
17 bool cmp(const point&A,const point&B){int d=cross(A,B);return !d?len(A)<len(B):d<0;}///    
18 bool judge(int i,int j){
19     for(int k=i+1;k<j;k++){
20         if(cross(q[j],q[k])!=0&&cross(q[i],q[k])!=0&&cross(q[j]-q[i],q[k]-q[i])<0)return false;
21     }
22     return true;
23 }///
24 void Max(int&x,int y){if(x<y)x=y;}//
25 int dp(){
26     sort(q+1,q+m+1,cmp);
27     for(int i=1;i<=m;i++)
28     for(int j=i+1;j<=m;j++){
29         if(judge(i,j))vis[i][j]=0;else vis[i][j]=1;
30     }
31     int res=0;
32     for(int i=1;i<=m;i++)
33     for(int j=1;j<i;j++)if(!vis[j][i]){
34         int s=cross(q[i],q[j]);f[j][i]=s;
35         for(int k=j-1;k>=1;k--){
36             if(cross(q[j]-q[k],q[i]-q[j])<0)Max(f[j][i],f[k][j]+s);
37             if(!cross(q[k],q[j]))break;
38         }
39         Max(res,f[j][i]);
40     }else f[j][i]=0;//
41     return res;
42 }////
43 int main()
44 {    //freopen("P.in","r",stdin);
45     //freopen("P.out","w",stdout);
46     int T;scanf("%d",&T);
47     while(T--){
48         scanf("%d",&n);
49         for(int i=1;i<=n;i++)scanf("%d%d",&p[i].x,&p[i].y);
50         int ans=0;
51         for(int i=1;i<=n;i++){ 
52             m=0;for(int j=1;j<=n;j++){if(p[i]<p[j])q[++m]=p[j]-p[i];}//
53             Max(ans,dp());
54         }//
55         printf("%.1lf\n",ans*0.5+(1e-8));
56     }//
57     return 0;
58 }//by tkys_Austin; 
View Code

 

 

 


Q  Chopsticks                 (贪心+转移优化dp)

因为有贡献的只有两个小的值,所以先不管这两个小的值后面的哪一个大的值;

YY一下可以发现这两个小的值一定挨在一起;

意思是选k个连续的i和i+1不重合,并且还可以在每个的后面补上一个比较大的数;

一个比较单纯的想法是dp[i][j][k]表示选到i选了j组还需要补k个大数;-》dp[n][m][0];   (应该会T);

但是倒着从后往前i>3*j再转移就直接dp[i][j]就可以了;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=5010;
 6 int T,n,k,a[N],f[N][N];
 7 void Min(int&x,int y){if(x>y)x=y;}
 8 int main()
 9 {    //freopen("Q.in","r",stdin);
10     //freopen("Q.out","w",stdout); 
11     scanf("%d",&T);
12     while(T--){
13         scanf("%d%d",&k,&n);k+=8;
14         for(int i=1;i<=n;i++)scanf("%d",&a[n-i+1]);
15         memset(f,0x3f,sizeof(f));
16         f[1][0]=0;
17         for(int i=1;i<=n;i++)
18         for(int j=0;j<=k;j++)if(i>j*3){
19             Min(f[i+1][j],f[i][j]);
20             Min(f[i+2][j+1],f[i][j]+(a[i]-a[i+1])*(a[i]-a[i+1]));
21         }
22         cout<<f[n+1][k]<<endl;
23     }
24     return 0;
25 }//by tkys_Austin; 
View Code

 

 


R  Pitcher Rotation           (贪心+状态设计dp)

dp[i][i1][i2][i3][i4]表示已经安排了i组,最后的几组是i1,i2,i3,i4;

可以转移,但是时空复杂度无法承受;

贪心地想其实因为最坏只会和前面四个球员都冲突,每次都只会选实力前五的球员;

所以i1=0,1,2,3,4,5;0表示没有球员参赛,复杂度变得可观;

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm> 
using namespace std;
const int N=501;
int n,m,g,f[N][10][10][10][10],t[N];
struct data{
    int p,id;
    bool operator<(const data&A)const{return (p==A.p)?id<A.id:p>A.p;}
}a[N][N];
void Max(int&x,int y){if(x<y)x=y;}
bool B(int g1,int p1,int g2,int p2){
    if(!p1||!p2||g1<0||g2<0)return 0;
    return a[t[g1]][p1].id==a[t[g2]][p2].id;
}
int main()
{    //freopen("R.in","r",stdin);
    //freopen("R.out","w",stdout);
    int T;scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&g);g+=10; 
        for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++){
            scanf("%d",&a[i][j].p);
            a[i][j].id=j;
        }
        for(int i=1;i<=g;i++)scanf("%d",&t[i]);
        for(int i=1;i<=m;i++)sort(a[i]+1,a[i]+n+1); 
        memset(f,0,sizeof(f));
        for(int i=0;i<g;i++)
        for(int j1=0;j1<=5;j1++)
        for(int j2=0;j2<=5;j2++)
        for(int j3=0;j3<=5;j3++)
        for(int j4=0;j4<=5;j4++){
            if(!t[i+1])Max(f[i+1][j2][j3][j4][0],f[i][j1][j2][j3][j4]);
            for(int k=1;k<=5;k++){
                if(B(i-3,j1,i+1,k)||B(i-2,j2,i+1,k)||B(i-1,j3,i+1,k)||B(i,j4,i+1,k))continue;
                Max(f[i+1][j2][j3][j4][k],f[i][j1][j2][j3][j4]+a[t[i+1]][k].p);
            }
        }
        int ans=0;
        for(int j1=0;j1<=5;j1++)
        for(int j2=0;j2<=5;j2++)
        for(int j3=0;j3<=5;j3++)
        for(int j4=0;j4<=5;j4++)Max(ans,f[g][j1][j2][j3][j4]); 
        printf("%.2lf\n",ans*0.01);
    }
    return 0;
}//by tkys_Austin; 
View Code

 

 


S  Garlands                   (二分+dp,小心陷阱)

不合法的情况比较好判断;

先二分一个最小的半边质量;check的时候无法贪心地选最长的,因为是要两两配对的;

注意到满足条件的时候有k段一定可以有k+2段,但不一定有k+1段(因为一段只能保证可以拆为三段);

dp[i][0/1]表示选到i,段数为奇或偶的最小段数;

判定条件:dp[i][m%2]<=mid;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=40100,inf=0x3f3f3f3f;
 6 int n,m,d,sum[N],f[N][2];
 7 int dp(int mx){
 8     memset(f,0x3f,sizeof(f));
 9     f[0][0]=0;
10     for(int i=2;i<=n;i+=2){
11         for(int j=1;j<=d&&(j<<1)<=i;j++){
12         if(sum[i]-sum[i-j]>mx||sum[i-j]-sum[i-2*j]>mx)continue;
13         for(int k=0;k<2;k++)f[i][k]=min(f[i][k],f[i-j*2][k^1]+1); 
14         }
15     }
16     return f[n][m%2]<=m;
17 }
18 int main()
19 {    //freopen("S.in","r",stdin);
20     //freopen("S.out","w",stdout);
21     int T;scanf("%d",&T);
22     while(T--){
23         scanf("%d%d%d",&n,&m,&d);m--;
24         for(int i=1;i<=n;i++)scanf("%d",&sum[i]),sum[i]+=sum[i-1];
25         if((n&1)||(n<m*2)||(n>m*2*d)||!d){puts("BAD");continue;}
26         int l=0,r=sum[n];
27         while(l<r){
28             int mid=(l+r)>>1;
29             if(dp(mid))r=mid;else l=mid+1;
30         }
31         cout<<l<<endl;
32     }
33     return 0; 
34 }//by tkys_Austin;
View Code

 

 


T  Mountain Road              (转移方式很关键,注意后效性)

dp[j][k]表示A边到j,B边到k的最短出去时间;

异向很好转移,一次转移枚举一个或多个都无所谓;

同向的话只能一次枚举多个车然后考虑他们一起开过去同时他们的出发和结束时间都相差至少10;

如果直接转移的话无法知道上一个车的起点,如果强行记录的话是不满足最优性的;(可以试试样例二);

总之O(n^3);

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=210,inf=0x3f3f3f3f;
 6 int n,n1,n2,A1[N],A2[N],B1[N],B2[N],f[N][N][2];
 7 char s[10];
 8 int main()
 9 {    //freopen("T.in","r",stdin);
10     //freopen("T.out","w",stdout);
11     int T;scanf("%d",&T);
12     while(T--){
13         scanf("%d",&n);
14         n1=n2=0;
15         for(int i=1;i<=n;i++){
16             scanf("%s",s);
17             if(s[0]=='A')n1++,scanf("%d%d",&A1[n1],&B1[n1]);
18             else n2++,scanf("%d%d",&A2[n2],&B2[n2]);
19         }
20         memset(f,0x3f,sizeof(f));
21         f[0][0][0]=f[0][0][1]=0;
22         for(int i=0;i<=n1;i++)
23         for(int j=0;j<=n2;j++){
24             int mxs=f[i][j][1],mxt=0;
25             for(int k=i+1;k<=n1;k++){
26                 mxs=max(mxs,A1[k]);
27                 mxt=max(mxt,mxs+B1[k]);
28                 f[k][j][0]=min(f[k][j][0],mxt);
29                 mxs+=10; mxt+=10; 
30             }
31             mxs=f[i][j][0],mxt=0; 
32             for(int k=j+1;k<=n2;k++){
33                 mxs=max(mxs,A2[k]);
34                 mxt=max(mxt,mxs+B2[k]);
35                 f[i][k][1]=min(f[i][k][1],mxt);
36                 mxs+=10; mxt+=10;
37             }
38         }
39         cout<<min(f[n1][n2][0],f[n1][n2][1])<<endl;
40     }
41     return 0;
42 }//by tkys_Austin; 
43 
44 
45 
46 //My previous code : Starting time is not considered in this algorithm;
47 //if(i)f[i][j][0]=min(max(A1[i]+B1[i],f[i-1][j][0]+10),max(A1[i],f[i-1][j][1])+B1[i]);
48 //if(j)f[i][j][1]=min(max(A2[j]+B2[j],f[i][j-1][1]+10),max(A2[j],f[i][j-1][0])+B2[j]);
View Code

 

 


U  Period                     (二分+dp,分段有技巧)

似乎跟LCS关系不大(我也不知道)

二分k的值为mid;

dp[i][j]表示x分到i位和y匹配到j位时最后一段的最小修改次数;

dp[i][j]  <-  dp[i][j-1]+1   直接一个数插入

dp[i][j]  <-  dp[i-1][j]+1  直接删除一个数

dp[i][j]  <-  dp[i-1][j-1]+(x[i]!=y[j])  修改或直接配对;

需要再加一个操作:dp[i][len(y)]<=mid , 则将dp[i][0]赋为0,否则inf,表示可不可以作为新的起点;

判断条件为dp[len(x)][len(y)]<=mid;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=5010,inf=0x3f3f3f3f;
 6 char x[N],y[N];
 7 int f[N][55],lx,ly;
 8 int dp(int k){
 9     memset(f,0x3f,sizeof(f));
10     f[0][0]=0;
11     for(int i=0;i<=lx;i++){
12         if(f[i][ly]<=k)f[i][0]=0; 
13         for(int j=0;j<=ly;j++)if(f[i][j]!=inf){
14             f[i+1][j+1]=min(f[i+1][j+1],f[i][j]+(x[i+1]!=y[j+1]));
15             f[i+1][j]=min(f[i+1][j],f[i][j]+1);
16             f[i][j+1]=min(f[i][j+1],f[i][j]+1);
17         }
18     }
19     return f[lx][ly]<=k;
20 }//
21 int main()
22 {    //freopen("U.in","r",stdin);
23     //freopen("U.out","w",stdout);
24     int T;scanf("%d%*c",&T);
25     while(T--){
26         gets(y+1),gets(x+1);
27         lx=strlen(x+1),ly=strlen(y+1);
28         int l=0,r=ly;
29         while(l<r){
30             int mid=(l+r)>>1;
31             if(dp(mid))r=mid;else l=mid+1; 
32         } 
33         cout<<l<<endl;
34     }
35     return 0;
36 }//by tkys_Austin; 
View Code

 


V  Matryoshka                 (简单的dp套dp)

不知道算不算套,不过不重要;

g[i][j][0/1]表示这段区间最后加入左或右端点那时的代价;

如果[i,j]不是从1连续的就赋值inf;

f[i][j]表示i到j的次数,每次从左端点或右端点,区间dp即可;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 using namespace std;
 5 const int N=510,inf=0x3f3f3f3f;
 6 int n,a[N],g[N][N],f[N],num[N],mx,tot,w[N][N][2],sum[N]; 
 7 int main()
 8 {    //freopen("V.in","r",stdin);
 9     //freopen("V.out","w",stdout);
10     while(scanf("%d",&n)!=EOF){
11         for(int i=1;i<=n;i++)scanf("%d",&a[i]);
12         for(int i=1;i<=n;i++){
13             for(int j=1;j<=n;j++)sum[j]=sum[j-1]+(a[j]>=a[i]);
14             for(int j=i+1;j<=n;j++)w[i][j][0]=min(j-i,sum[j]-sum[i-1]);
15             for(int j=i-1;j>=1;j--)w[j][i][1]=min(i-j,sum[i]-sum[j-1]);
16         }//
17         for(int i=1;i<=n;i++)g[i][i]=0;
18         for(int len=2;len<=n;len++)
19         for(int i=1;i+len-1<=n;i++){
20             int j=i+len-1;
21             g[i][j]=min(g[i+1][j]+w[i][j][0],g[i][j-1]+w[i][j][1]);
22         }//
23         for(int i=1;i<=n;i++){
24             memset(num,0,sizeof(num));
25             mx=0;tot=0;
26             for(int j=i;j<=n;j++){
27                 mx=max(mx,a[j]);
28                 if(!num[a[j]])num[a[j]]=1,tot++;
29                 if(j-i+1!=tot||mx!=j-i+1)g[i][j]=inf;//
30             }
31         }
32         memset(f,0x3f,sizeof(f)); 
33         f[0]=0;
34         for(int i=1;i<=n;i++){
35             for(int j=1;j<=i;j++)f[i]=min(f[i],g[j][i]+f[j-1]);//
36         }
37         if(f[n]!=inf)cout<<f[n]<<endl;
38         else puts("impossible"); 
39     }
40     return 0;
41 }//by tkys_Austin;
View Code

 


W  Minimizing Maximizer       (dp+线段树)

意思是选编号从小到大的最少的线段使得可以连续地覆盖整个区间(和普通区间覆盖有差别);

dp[i]表示到第i号线段1-r[i]都已经完全覆盖的最小个数;

dp[i]=min{dp[j]+1}(j<i且,r[i]>=r[j]>=l[i]);显然过不了;

观察到转移范围是一个连续的区间[ l[i] , r[i] ],可以用线段树维护每次的最大值,做完再把当前的dp[i]update到r[i]上去;

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #define ls (k<<1)
 5 #define rs ((k<<1)|1)
 6 using namespace std;
 7 const int N=50010,M=500001,inf=0x3f3f3f3f;
 8 int n,m,L[M],R[M],tr[N<<2],f[M];
 9 void build(int k,int l,int r){
10     tr[k]=inf;
11     if(l==r){return;}
12     int mid=(l+r)>>1;
13     build(ls,l,mid);
14     build(rs,mid+1,r);
15 }
16 void pushup(int k){tr[k]=min(tr[ls],tr[rs]);}
17 void update(int k,int l,int r,int x,int y){
18     if(l==r)tr[k]=min(tr[k],y);
19     else {
20         int mid=(l+r)>>1;
21         if(x<=mid)update(ls,l,mid,x,y);
22         else update(rs,mid+1,r,x,y);
23         pushup(k);
24     }
25 }
26 int query(int k,int l,int r,int x,int y){
27     if(l==x&&r==y)return tr[k];
28     else {
29         int mid=(l+r)>>1;
30         if(y<=mid)return query(ls,l,mid,x,y);
31         else if(x>mid)return query(rs,mid+1,r,x,y);
32         else return min(query(ls,l,mid,x,mid),query(rs,mid+1,r,mid+1,y));
33     }
34 }
35 int main()
36 {    //freopen("W.in","r",stdin);
37     //freopen("W.out","w",stdout);
38     int T;scanf("%d",&T);
39     while(T--){
40         scanf("%d%d",&n,&m);
41         for(int i=1;i<=m;i++)scanf("%d%d",&L[i],&R[i]);
42         build(1,1,n);
43         update(1,1,n,1,0);
44         int ans=inf;
45         for(int i=1;i<=m;i++){
46             f[i]=query(1,1,n,L[i],R[i])+1;
47             if(R[i]==n)ans=min(ans,f[i]);
48             update(1,1,n,R[i],f[i]);
49         }
50         cout<<ans<<endl;
51         if(T)cout<<endl; 
52     }
53     return 0;
54 }//by tkys_Austin; 
View Code

 

posted @ 2018-08-22 09:10  大米饼  阅读(370)  评论(1编辑  收藏  举报