动态规划博客2

P1233 木棍加工 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

贪心+dp,经典LIS,看了题解才知道还有这样一个定理

dilworth定理:不下降子序列最小个数等于最大上升子序列的长度。

 #include <bits/stdc++.h>
 using namespace std;
 int n;
 struct node{
     int a,b;
 };
 node x[5001];
 int dp[5001];
 bool com(node &a,node&b){
     if(a.a==b.a){
         return a.b>b.b;
     }
     return a.a>b.a;
 }
 int LIS(){
     int ans=0;
     for(int i=1;i<n;i++){
         for(int k=i-1;k>=0;k--){
             if(x[i].b>x[k].b){
                 dp[i]=max(dp[i],dp[k]+1);
             }
         }
         ans=max(ans,dp[i]);
     }
     return ans;
 }
 int main(){
 ​
     cin>>n;
     for(int i=0;i<n;i++){
         cin>>x[i].a>>x[i].b;
         dp[i]=1;
     }
     sort(x,x+n,com);
     cout<<LIS()<<endl;
     return 0;
 }

[P1091 NOIP2004 提高组] 合唱队形 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

俺也不知道这题算不算dp,俺也不敢问,

分别把每个数当作中间最大的那个数,求出其左侧最多的递减的数的个数和其右侧最多的递增的数的个数,相加最大的就是答案。

 #include<bits/stdc++.h>
 using namespace std;
 int g[105],f[105],a[105],s[105];
 int main()
 {
     int n;
     cin>>n;
     for(int i=1;i<=n;i++){
         cin>>a[i];
         f[i]=1;
         g[i]=1;
     } 
     for(int i=n-1;i>=1;i--){
         for(int j=i+1;j<=n;j++){
             if(a[i]>a[j]){
                 f[i]=max(f[i],f[j]+1);
             }
         }
     } 
     for(int i=2;i<=n;i++){
         for(int j=1;j<i;j++){
             if(a[i]>a[j])
             {
                 g[i]=max(g[i],g[j]+1);
             }
         }
     } 
     int maxx=0;
     for(int i=1;i<=n;i++)
     {
         s[i]=f[i]+g[i]-1;
         maxx=max(s[i],maxx);
     }
     cout<<n-maxx;
 }

Problem - C - Codeforces (Unofficial mirror site, accelerated for Chinese users)

一道二维前缀和dp,但是没做出来,看了一年不知道自己哪里写的不对,www,先挂着等我变强了再回来看吧,毕竟1700,现在我还不配。

 #include <bits/stdc++.h>
 using namespace std;
 int mp[401][401];
 //二维前缀和,代表从1,1开始长为a宽为b的矩形中1的数量 
 int dp[401][401];
 int main(){
     int t;
     cin>>t;
     while(t--){
         int n,m;
         cin>>n>>m;
         for(int i=1;i<=n;i++){
             for(int k=1;k<=m;k++){
                 char t;
                 cin>>t;
                 mp[i][k]=t-'0';
             }
             memset(dp[i],0,sizeof(dp[i]));
         }
         for(int i=1;i<=n;i++){
             for(int k=1;k<=m;k++){
                 dp[i][k]=dp[i-1][k]+dp[i][k-1]-dp[i-1][k-1]+mp[i][k];
                 //cout<<dp[i][k]<<" "; 
             }
             //cout<<endl;
         }
         int ans=0x3f3f3f3f;
         for(int i=1;i<=n;i++){
             for(int k=1;k<=m;k++){
                 for(int s=i+4;s<=n;s++){
                     for(int j=k+3;j<=m;j++){
                         //以(i,k)(s,j)为顶点的矩形的四条边的0的个数加上中心1的个数
                         int line1=dp[s][j]-dp[s][j-1]-mp[s][j]-(dp[i][j]-dp[i][j-1]);
                         int line2=dp[i][j]-dp[i-1][j]-mp[i][j]-(dp[i][k]-dp[i-1][k]);
                         int line3=dp[s][k]-dp[s][k-1]-mp[s][k]-(dp[i][k]-dp[i][k-1]);
                         int line4=dp[s][j]-dp[s-1][j]-mp[s][j]-(dp[s][k]-dp[s-1][k]);
                         int mid=dp[s][j]-dp[s][k]-dp[i][j]+dp[i][k]-line1-line4-mp[s][j];               
                         int t=(j-k-1)*2+(s-i-1)*2;
                         t=t-line1-line2-line3-line4;
                         t+=mid;
                         //cout<<line1<<" "<<line2<<" "<<line3<<" "<<line4<<" "<<mid<<endl;
                         if(t>ans){
                             break;
                         }
                         ans=min(t,ans);
                     }
                 }
             }
         } 
         cout<<ans<<endl;
     }
     
     return 0;
 }

Problem - D - Codeforces (Unofficial mirror site, accelerated for Chinese users)

不算很难的一题,但是羸弱的我还是没想出来。

其实这题只用想清楚两种情况就行了

1-不删数,最长连续上升子序列

2-删数,假设删除a[i],那么有可能对结果产生影响的唯一情况就是a[i-1]<a[i+1]时,新结果为以a[i-1]为结尾的最长连续上升子序列+以a[i+1]为起点的最长连续上升子序列

 #include <bits/stdc++.h>
 using namespace std;
 int x[200001];
 int LIS1[200001];
 int LIS2[200001];
 int main(){
     int n;
     cin>>n;
     int ans=0;
     for(int i=0;i<n;i++){
         cin>>x[i];
         LIS1[i]=1;
         LIS2[i]=1;
     }
     for(int i=1;i<n;i++){
         if(x[i]>x[i-1]){
             LIS1[i]=LIS1[i-1]+1;
         }
         ans=max(ans,LIS1[i]);
     }
     for(int i=n-2;i>=0;i--){
         if(x[i]<x[i+1]){
             LIS2[i]=LIS2[i+1]+1;
         }
         ans=max(ans,LIS1[i]);
     }   
     for(int i=0;i<n-2;i++){
         if(x[i]<x[i+2]){
             ans=max(ans,LIS1[i]+LIS2[i+2]);
         }
     }
     cout<<ans<<endl;
     return 0;
 }

Problem - 1492C - Codeforces (Unofficial mirror site, accelerated for Chinese users)

哎,人麻了,看了题解恍然大悟,为什么自己就想不到呢,看来还是欠点火候

准备两个数组,pre代表b中每个字符可以匹配的最左位置

en代表b中的每个字符可以匹配的最右的位置

而答案一定产生在相邻的两个b的字符中,即前一个最左,后一个最右,遍历取差最大即可

 #include <bits/stdc++.h>
 using namespace std;
 int pre[200001];
 int en[200001];
 int main(){
     int n,m;
     cin>>n>>m;
     string a,b;
     cin>>a>>b;
     int pos=0;
     for(int i=0;i<a.size();i++){
         while(a[i]!=b[pos]){
             i++;
         }       
         pre[pos]=i;
         pos++;
         if(pos==b.size()){
             break;
         }
     }
     pos=b.size()-1;
     for(int i=a.size()-1;i>=0;i--){
         while(a[i]!=b[pos]){
             i--;
         }       
         en[pos]=i;
         pos--;
         if(pos==b.size()){
             break;
         }
     }
     int ans=0;
     for(int i=1;i<b.size();i++){
         ans=max(ans,en[i]-pre[i-1]);
     }
     cout<<ans<<endl;
     return 0;
 }

1463 -- Strategic game (poj.org)

树形dp入门题目,周赛a题,听了zwt大人的讲解,我恍然大悟,题目大意是给你一颗树,你可以在其中选择任意数量的结点放上哨兵,而后以该点为端点的边就会被监视,求最少放置多少个哨兵可以让所有的边都被覆盖到

思路:树上dp问题,我们从根节点开始考虑,无非就是两种情况,我选择了根节点和我没选择根节点(就是放置哨兵的意思)

1-选择根节点,而后与它相邻的各边都被覆盖,那他的儿子们上放不放哨兵都无所谓了

2-不选择根节点,那他的儿子每一个都必须放上了哨兵

由此可以定义两个维度dp[n] [2],dp[i] [0]代表以第i个结点为根节点,不选择第i个结点所需要的最少哨兵数,dp[i] [1]代表以第i个结点为根节点,选择第i个结点所需要的最少哨兵数,所以状态转移方程就是

其中n是u的孩子,num是u的儿子个数

实现的时候就在dfs的回溯过程中更新dp就可以了(树上dfs时间复杂度就是O(n))

 #include <iostream>
 #include <cstdio>
 #include <vector>
 #include <string.h>
 using namespace std;
 vector<int>mp[1501];
 int in[1501];
 int dp[1501][2];
 int n;
 ​
 void dfs(int now){
     if(mp[now].size()==0){
         dp[now][0]=0;
         dp[now][1]=1;
         return;
     }
     for(int i=0;i<mp[now].size();i++){
         dfs(mp[now][i]);
     }   
     for(int i=0;i<mp[now].size();i++){
         dp[now][1]+=min(dp[mp[now][i]][1],dp[mp[now][i]][0]);
         dp[now][0]+=dp[mp[now][i]][1];
     }
     dp[now][1]++;
 }
 ​
 int main(){ 
     while(scanf("%d",&n)!=EOF){
         memset(in,0,sizeof(in));
         memset(mp,0,sizeof(mp));
         memset(dp,0,sizeof(dp));
         for(int i=0;i<n;i++){
             int x;
             char t;
             scanf("%d",&x);
             scanf("%c%c",&t,&t);
             int num;
             scanf("%d",&num);
             scanf("%c",&t);
             for(int k=0;k<num;k++){
                 int y;
                 scanf("%d",&y);
                 mp[x].push_back(y);
                 in[y]++; 
             }
         }
         int root=0;
         for(int i=0;i<n;i++){
             //cout<<in[i]<<" ";
             if(in[i]==0){
                 root=i;
                 break;
             }
         }
         dfs(root);
         printf("%d\n",min(dp[root][0],dp[root][1]));
     }
     
     return 0;
 }

总结:周赛运气成分居多,1800的题居然都蒙出来了,平时1500的题都不会做,呜呜呜,国庆也没咋训练就水过去了,哎,还因为宿舍分痛失奖学金,fpx还四连跪,艹,心态崩了,下周考虑考虑继续dp,还是开字符串,mua的区间dp刷不动了,树上dp学不会。

Guess you like

Origin blog.csdn.net/m0_58178876/article/details/120797590