zoukankan      html  css  js  c++  java
  • codeforces每日一题1-15

    目录:

    1.1093D. Beautiful Graph(DFS染色)

    2.514C - Watto and Mechanism(Tire)

    3.69E.Subsegments(STL)

    4.25C. Roads in Berland(最短路松弛操作)

    5.128B. String Problem (floyd预处理)

    6.33C.Wonderful Randomized Sum(思维)

    7.1292B.Aroma's Search (暴力+思维)

    8.1286 A.Garland(DP)

    9.1285D Dr. Evil Underscores (01Trie+DFS)

    10.631C. Report(单调栈)

    11.1119D. Frets On Fire (二分+排序)

    12.1234D. Distinct Characters Queries(STL/线段树)

    13.1073C. Vasya and Robot(二分+前缀和)

    14.455A. Boredom(DP+map)

    15.225C. Barcode(DP)

    1093D. Beautiful Graph(DFS染色)

    题意:

    给你一个一个n个顶点,m条边的无向图,你可以将顶点赋值为1,2,3,现在问你有多少种赋值方案,使得任意一条边的两个顶点权值和为奇数

    思路:

    由于权值和要为奇数,所有一条边的两个顶点要一个为奇数,一个为偶数,所以就变成了一个染色问题

    要注意图并不一定联通,所以答案为每个连通块内(2^(奇数)+2^(偶数))的乘积和-可以由组合数进行推导

    如果有任意一个块无法染色,则答案为0

    #include<iostream>
    #include<algorithm>
    #include<vector>
     using namespace std;
     typedef long long ll;
     const ll mod=998244353;
     const int maxn=3e5+10;
     vector<int> a[maxn];
     int num1,num2,color[maxn],n=0,flag,two[maxn];
     void pre_process()
     {
         two[0]=1;
         for(int i=1;i<maxn;i++)
             two[i]=(2*two[i-1])%mod;
     }
     void dfs(int x,int fa,int co)
     {
         if(!flag) return;
         color[x]=co;
         if(co==1) num1++;
         else num2++;
         for(int i=0;i<a[x].size();i++){
            int to=a[x][i];
            if(color[to]==color[x])
            {
                flag=0;return ;
            }
            if(to!=fa&&color[to]==0)
            {
                if(co==1) dfs(to,x,2);
                else dfs(to,x,1);
            }
         }
     }
     int main()
     {
         int t,m,u,v;
         ll ans=0;
         scanf("%d",&t);
         pre_process();
         while(t--){
             for(int i=1;i<=n;i++) a[i].clear(),color[i]=0;
             num1,num2,ans=1;
             flag=1;
             scanf("%d%d",&n,&m);
             for(int i=1;i<=m;i++){
                 scanf("%d%d",&u,&v);
                 a[u].push_back(v);
                 a[v].push_back(u);
             }
            for(int i=1;i<=n;i++){
                if(!flag) break;
                if(!color[i]){
                    num1=num2=0;
                    dfs(i,-1,1);
                    ans*=(two[num1]+two[num2])%mod;
                    ans%=mod;
                }
            }
            if(!flag) cout<<0<<endl;
            else cout<<ans%mod<<endl;
         }
         return 0;
     }
    View Code

    514C - Watto and Mechanism(Tire)

    题意:

    给n个模式串,m个匹配串,问是否有只与匹配串相差一个字符的模式串

    思路:

    直接上Tire,正常插入模式串

    匹配时往该节点的儿子节点进行正常匹配,若能匹配成功,就输出YES

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<string>
     using namespace std;
     const int maxn=1e6+10;
     int n,m;
     struct Tire{
         int ch[maxn][3],val[maxn],cnt;
         void init()
         {
             cnt=1;
             memset(ch,0,sizeof(ch));
             val[0]=0;
         }
         void insert(string s){
            int u=0,n=s.length();
            for(int i=0;i<n;i++){
                int c=s[i]-'a';
                if(!ch[u][c]) ch[u][c]=cnt++;
                u=ch[u][c];
            }
            val[u]=cnt;
        }
        bool query(string s){
            int u=0,n=s.length(),flag,k;
            for(int i=0;i<n;i++){
                int c=s[i]-'a';
                for(int j=0;j<=2;j++){
                    if(c==j||ch[u][j]==0) continue;
                    int x=ch[u][j];
                    flag=1;
                    for(int k=i+1;k<n;k++){
                        int y=s[k]-'a';
                        if(!ch[x][y]){
                            flag=0;
                            break;
                        }
                        x=ch[x][y];
                    }
                    if(flag&&val[x]) return true;
                }
                if(!ch[u][c]) return false;
                u=ch[u][c];
            }
            return false;
        }
     }T;
     int main()
     {
         string temp;
        scanf("%d%d",&n,&m);
        T.init();
         for(int i=1;i<=n;i++){
             cin>>temp;
             T.insert(temp);
         }
        for(int i=1;i<=m;i++){
            cin>>temp;
            if(T.query(temp)) cout<<"YES"<<endl;
            else cout<<"NO"<<endl;
        }
        return 0;
     }
    View Code

    69E.Subsegments(STL)

    题意:

    输出每一个长度为K的区间中出现次数不为1数的最大值

    思路:

    维护一个map与set

    set用于维护当前所指向的区间(可以O(logn)直接取到最大值与删除容器内元素)

    map用于维护区间内元素的出现个数

    先将前k个元素加入set内,每个元素加入之后,通过map查询其出现次数,如果不为1则抹去,最后判断set是否为空,如果不为空则输出set内的最后一个元素(set容器内元素有序)

    之后移动区间,需要删去一个元素这里要注意如果元素删去后其出现次数为1的话,要将其重新加入区间内

    #include<iostream>
    #include<algorithm>
    #include<map>
    #include<set>
     using namespace std;
     const int maxn=1e5+10;
     map<int,int> cnt;
     set<int> s;
     int a[maxn];
     int main()
     {
         int n,k;
         scanf("%d%d",&n,&k);
         for(int i=1;i<=n;i++)    scanf("%d",&a[i]);
         for(int i=1;i<=k;i++){
             cnt[a[i]]++;
             if(cnt[a[i]]!=1) s.erase(a[i]);
             else s.insert(a[i]);
         }
         if(s.empty())    cout<<"Nothing"<<endl;
         else    cout<<*(--s.end())<<endl;
         for(int i=k+1;i<=n;i++){
             cnt[a[i]]++;
             cnt[a[i-k]]--;
             if(cnt[a[i]]!=1)    s.erase(a[i]);
             else    s.insert(a[i]);
             if(cnt[a[i-k]]==1)    s.insert(a[i-k]);
             else    s.erase(a[i-k]);
             if(s.empty())    cout<<"Nothing"<<endl;
             else    cout<<*(--s.end())<<endl;
         }
        return 0;
     }
    View Code

    25C. Roads in Berland(最短路松弛操作)

    题意:

    给你一个dis[i][j]矩阵,代表从i顶点到j顶点的最短路,再给你k条路,每次将其加入地图中,问加入后各个顶点之间最短路的和

    思路:

    如果加入的道路距离比原来的距离长,则不需要修改答案

    如果小于原来的距离,则先将原来的距离更新,枚举起点与重点判断能否进行松弛操作

    松弛操作:dis[i][j]>dis[i][v]+w+dis[u][j]

         ②dis[i][j]>dis[i][u]+w+dis[v][j]

    #include<iostream>
    #include<algorithm>
    #include<cstring>
     using namespace std;
     typedef long long ll;
     const int maxn=305;
     ll dis[maxn][maxn];
     ll n,ans=0,m,u,v,w;
     void solve()
     {
         for(int i=1;i<=n;i++){
             for(int j=1;j<=n;j++){
                if(dis[i][j]>dis[i][v]+w+dis[u][j]){
                    ans-=dis[i][j]-(dis[i][v]+w+dis[u][j]);
                    dis[i][j]=dis[j][i]=dis[i][v]+w+dis[u][j];
                }
                if(dis[i][j]>dis[i][u]+w+dis[v][j]){
                    ans-=dis[i][j]-(dis[i][u]+w+dis[v][j]);
                    dis[i][j]=dis[j][i]=dis[i][u]+w+dis[v][j];
                 }
         }
     }
    }
     int main()
     {
         scanf("%lld",&n);
         for(int i=1;i<=n;i++)
             for(int j=1;j<=n;j++){
                 scanf("%lld",&dis[i][j]);
                 ans+=dis[i][j];
             }
        ans/=2;
        scanf("%lld",&m);
        for(int i=1;i<=m;i++){
            scanf("%lld%lld%lld",&u,&v,&w);
            if(dis[u][v]<=w){
                cout<<ans<<" ";
                continue;
            }
            else{
                ans-=dis[u][v]-w;
                dis[u][v]=dis[v][u]=w;
                solve();
                cout<<ans<<" ";
            }
        }
        return 0;
     }
    View Code

    128B. String Problem (floyd预处理)

    题意:

    给两个字符串,以及m个从字符a变换到b的代价,问把两个字符串变得相同的最小代价

    思路:

    将字符串变换的代价当做一条有向边建图,之后跑floyd,得到从字符i变成j的最小代价dis[i][j]

    之后遍历两个字符串的每一位,不相同的话枚举变成什么字符所需要的代价最小,如果找不到这样一个中间点使得s1[i]=ch,s2[i]=ch,则直接输出-1,最后将最小代价累加就好了

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #define inf 0x3f3f3f3f
     using namespace std;
     const int maxn=1e5+10;
     int dis[27][27],mp[27][27];
     char s1[maxn],s2[maxn],a1,a2,s3[maxn];
     void floyd()
     {
         for(int i=0;i<26;i++)
             for(int j=0;j<26;j++)    dis[i][j]=mp[i][j];
         for(int k=0;k<26;k++)
             for(int i=0;i<26;i++)
                 for(int j=0;j<26;j++)
                     dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
     }
     int main()
     {    
         int n,w;
         cin>>s1>>s2>>n;
         for(int i=0;i<26;i++)
             for(int j=0;j<26;j++){
                 if(i==j)    mp[i][j]=0;
                 else mp[i][j]=inf;
             }
         for(int i=1;i<=n;i++){
             cin>>a1>>a2>>w;
             int u=a1-'a',v=a2-'a';
             mp[u][v]=min(mp[u][v],w);
         }
        floyd();
        int len1=strlen(s1),len2=strlen(s2);
        if(len1!=len2){
            cout<<-1<<endl;
            return 0;
        }
        int ans=0;
        for(int i=0;i<len1;i++){
            if(s1[i]==s2[i]){s3[i]=s1[i];continue;}
            else{
                int mini=inf,x=-1,u=s1[i]-'a',v=s2[i]-'a';
                for(int j=0;j<26;j++){
                    if(dis[u][j]+dis[v][j]<mini) mini=dis[u][j]+dis[v][j],x=j;
                }
                if(x==-1){
                    cout<<"-1"<<endl;
                    return 0;
                }
                else{
                    s3[i]=x+'a';
                    ans+=mini;
                }
            }
        }
        cout<<ans<<endl<<s3<<endl;
        return 0;
     }
    View Code

     33C.Wonderful Randomized Sum

    题意:

    给一个数组你可以把一段前缀和乘上-1,再把一个后缀和乘上-1,前缀跟后缀之间可以有重叠部分,问操作后序列的最大和是多少

    思路:

    我们先考虑有重叠的部分,由于乘了两次-1,所有重合的部分不变,因此答案的方案肯定不会有重叠部分

    我们假设所有元素的和为sum,从反面思考假设中间的那段和为s,那么操作后数组的和为-(sum - s) + s = 2 * s - sum

    所以问题就转化为找一个最大子序列问题了

    #include<iostream>
    #include<algorithm>
     using namespace std;
     const int maxn=1e5+10;
     int a[maxn];
     int main()
     {
         int n,sum=0;
         scanf("%d",&n);
         for(int i=1;i<=n;i++){
             scanf("%d",&a[i]);
             sum+=a[i];
         }
         int mx,cnt=0;
         for(int i=1;i<=n;i++){
             cnt+=a[i];
             if(cnt<0) cnt=0;
             mx=max(mx,cnt);
         }
        cout<<2*mx-sum<<endl;
        return 0;
     }
    View Code

     1292B.Aroma's Search (暴力+思维)

    题意:

    告诉一个点的位置,之后的点按照x[i]=x[i-1]*ax+bx;,y[i]=y[i-1]*ay+by的规律分布,初始时你站在一个位置,每秒可以往四个方向移动,问你在t内最多能走过多少个点

    思路:

    通过数据我们可以发现,在264就已经超过了1e16也就是t的范围了,因此地图内最多只有64个点

    所以我们只需要暴力求出每一个点的坐标,再枚举你所走的点的起点与终点(只往一个方向走连续的点肯定是最优)

    判断距离(从初始位置走到起点的距离再加上起点到终点的距离)是否小于t即可,求出一个最大值就是答案啊

    #include<iostream>
    #include<algorithm>
    #include<cmath>
     using namespace std;
     typedef long long ll;
     ll x[70],y[70];
     ll dis(ll x1,ll y1,ll x2,ll y2)
     {
         return abs(x1-x2)+abs(y1-y2);
     }
     int main()
     {
         ll ax,ay,bx,by,xs,ys,t;
         int cnt=0;
         cin>>x[0]>>y[0]>>ax>>ay>>bx>>by;
         cin>>xs>>ys>>t;
         while(x[cnt]-xs<t&&y[cnt]-ys<t){
             cnt++;
             x[cnt]=x[cnt-1]*ax+bx;
             y[cnt]=y[cnt-1]*ay+by;
         }
        int ans=0;
        for(int i=0;i<=cnt;i++){
            for(int j=i;j<=cnt;j++){
                if(j-i+1<=ans) continue;
                ll temp=min(dis(x[i],y[i],xs,ys),dis(x[j],y[j],xs,ys));
                temp+=dis(x[i],y[i],x[j],y[j]);
                if(temp<=t) ans=j-i+1;
            }
        }
        cout<<ans<<endl;
        return 0;
     }
    View Code

    1286 A.Garland(DP)

    题意:

    有一个数组,包含乱序1-n的元素,并且有些数被从数组中拿出,现在问怎样将这些数放回才能使,相邻两数为奇偶的对数最小

    思路:

    定义dp[i][j][k]为到n这个位置放了j个偶数最后一位为奇数或偶数

    那么转移方程为:

    令t=dp[i-1][j][k]

    i.奇数:dp[i][j][1]=max(dp[i][j][1],t+(k!=1))

    ii.偶数:dp[i][j+1][0]=max(dp[i][j][0],t+(k!=0))

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #define inf 0x3f3f3f3f
     using namespace std;
     const int maxn=110;
     int dp[maxn][maxn][3],a[maxn],n;
     int main()
     {
         int n,num=0;
         scanf("%d",&n);
         for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        num=n/2;
        memset(dp,inf,sizeof(dp));
        dp[0][0][0]=dp[0][0][1]=0;
        for(int i=1;i<=n;i++){
            for(int j=0;j<i;j++){
                for(int k=0;k<2;k++){
                    int t=dp[i-1][j][k];
                    if(a[i]){
                        if(a[i]%2) dp[i][j][1]=min(dp[i][j][1],t+(k!=1));
                        else dp[i][j+1][0]=min(dp[i][j+1][0],t+(k!=0));
                    }
                    else{
                        for(int l=0;l<2;l++)
                            dp[i][j+1-l][l]=min(dp[i][j+1-l][l],t+(l!=k));
                    }
                }
            }
        }
        printf("%d
    ", min(dp[n][num][0], dp[n][num][1]));
     }
    View Code

    1285D Dr. Evil Underscores (01Trie+DFS)

    题意:

    给一个数组a,现在让你找到一个x使得max(x^ai)最小

    思路:

    建立起一颗Tire,对Tire进行DFS

    如果对于当前位,即有0又有1,则对左右两个子树分别DFS并取最小值

    当前位没有值的话,直接返回0

    如果只有0或者1,那就对相应子树进行DFS

    #include<iostream>
    #include<algorithm>
     using namespace std;
     typedef long long ll;
     const int maxn=2e6+10;
     struct Tire{
         int cnt,ch[maxn][2];
        void insert(int x)
         {
             int u=0;
             for(int i=30;i>=0;i--){
                 bool v=(1<<i)&x;
                 if(!ch[u][v]){
                     ch[u][v]=++cnt;
                 } 
                 u=ch[u][v];
             }
         }
        int dfs(int x,int dep)
        {
            int k1=ch[x][0],k2=ch[x][1];
            if(!k1&&!k2) 
                return 0;
            if(k1&&!k2)
                return dfs(k1,dep-1);
            if(k2&&!k1)
                return dfs(k2,dep-1);
            return (1<<dep)+min(dfs(k1,dep-1),dfs(k2,dep-1));
        }
    
     }T;
     int main()
     {
         int n,temp;
         scanf("%d",&n);
         for(int i=1;i<=n;i++){
             scanf("%d",&temp);
             T.insert(temp);
         }
        cout<<T.dfs(0,30)<<endl;
     }
    View Code

    631C. Report(单调栈)

    题意:

    给你一个数组,并且进行若干次操作,每次操作可以将1-R,升序或者降序排列,求出经过若干次操作后的数组

    思路:

    对于一些操作,比如i < j并且R< Rj那么i操作是没有意义的,因此我们可以建立一个单调栈,将操作次数进行缩减,并得到一个以R降序的操作序列,最大的操作区间长度为len

    现在将a数组的前len个元素复制到b数组,并且排序

    之后从右向左向a数组中按照每次操作的排序方式填数即可

    #include<iostream>
    #include<algorithm>
    #include<cstring>
     using namespace std;
     const int maxn=2e5+10;
     int a[maxn],b[maxn];
     struct query{
         int t,r;
     }c[maxn];
     int main()
     {
         int n,m,s=0,t,r,l;
         scanf("%d%d",&n,&m);
         for(int i=0;i<n;i++) scanf("%d",&a[i]);
         for(int i=1;i<=m;i++){
             scanf("%d%d",&t,&r);
             while(s>0&&r>=c[s-1].r) s--;
             c[s].t=t,c[s].r=r,s++;
         }
        l=0,r=c[0].r,c[s].r=0,s++; 
        for(int i=0;i<r;i++) b[i]=a[i];
        sort(b,b+r);
        for(int i=1;i<s;i++)
            for(int j=c[i-1].r;j>c[i].r;j--)
                a[j-1]=(c[i-1].t==1)?b[--r]:b[l++];
        for(int i=0;i<n;i++) cout<<a[i]<<" ";
        return 0;
     }
    View Code

    1119D. Frets On Fire (二分+排序)

    题意:

    给n个数Ai,那么矩阵Ci,j = Ai + j(j≤1e18),现在给q个询问,每次询问这个矩阵l列到j列有多少个不同的元素

    思路:

    首先对a数组排序去重,显然这对答案不会有影响

    对于排序去重之后数组中的两个相邻元素,如果他们之间的差值大于r-l+1,那么更小的那行对答案的贡献就为r-l+1,否则就为差值

    因此我们求出一差分数组,并在此排序,求出前缀和sum数组

    对于每次询问,我们用二分在差分数组中找到最后一个小于r-l+1的位置pos,那么本次查询的答案就为sun[pos] + (len-pos+1)*(r-l+1)

    #include<iostream>
    #include<algorithm>
     using namespace std;
     typedef long long ll;
     const int maxn=1e5+10;
     ll a[maxn],b[maxn],sum[maxn],l,r,ans[maxn];
     int n,q;
     int main()
     {
         scanf("%d",&n);
         for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
         sort(a+1,a+1+n);
         int len=unique(a+1,a+1+n)-a-1;
         for(int i=1;i<len;i++) b[i]=a[i+1]-a[i];
         sort(b+1,b+len);
         for(int i=1;i<len;i++) sum[i]=sum[i-1]+b[i];
        scanf("%d",&q);
        for(int i=1;i<=q;i++){
            scanf("%lld%lld",&l,&r);
            int li=1,ri=len-1;
            ll ret=r-l+1;
            while(li<=ri){
                int mid=(li+ri)>>1;
                if(b[mid]<=r-l+1) li=mid+1;
                else ri=mid-1;
            }
            li--;
            ret+=(sum[li]+(len-li-1)*(r-l+1));
            cout<<ret<<" ";
        }
     }
    View Code

    1234D. Distinct Characters Queries(STL/线段树)

    题意:

    给一个字符串,两种操作:1.进行单点修改2.询问区间l-r有多少个不同的字母

    思路:

    方法一:建立26棵线段树

    方法二:建立26个set,每个set存相应字母出现的位置

        对于修改,先在原来的字母对应的set中删除位置,再在更新的字母对应的set中插入位置

        由于set中元素都为有序的,所以每次询问,在每个set中进行二分找l如果找到的元素小于等于r答案就加一

    #include<iostream>
    #include<algorithm>
    #include<set>
     using namespace std;
     set<int> a[26];
     string temp;
     int main()
     {
        cin>>temp;
        int n,op,pos,l,r;
        char ch;
        for(int i=0;i<temp.size();i++) a[temp[i]-'a'].insert(i+1);
        scanf("%d",&n);
        while(n--){
            scanf("%d",&op);
            if(op==1){
                cin>>pos>>ch;
                a[temp[pos-1]-'a'].erase(pos);
                temp[pos-1]=ch;
                a[temp[pos-1]-'a'].insert(pos);
            }
            else{
                cin>>l>>r;
                int num=0;
                for(int i=0;i<26;i++){
                    set<int>::iterator it;
                    it=a[i].lower_bound(l);
                    if(it!=a[i].end()&&*it<=r) num++;
                }
                cout<<num<<endl;
            }
        }
        return 0;
     }
    View Code

     1073C. Vasya and Robot(二分+前缀和)

    题意:

    给你一串移动指令,与一个目的地,你可以对指令进行修改,每次修改的花费为,最右边的修改位置减去最左边的修改位置+1,让你求最少花费

    思路:

    二分答案进行判断,如何判断呢?

    先用前缀和记录下每段指令中每个方向移动的次数

    每次只需要判断x-(abs(ex-cntx)+abs(ey-cnty))是否为2的倍数即可,因为多出来的部分可以走相反的方向进行抵消,所以判断是否为2的倍数

    其中cntx为为改变区域中在x方向上的移动距离,以x轴正方向为正,cnty类似

    #include<iostream>
    #include<algorithm>
    #include<map>
    #include<cmath>
     using namespace std;
     const int maxn=2e5+10;
     map<char,int> m;
     int sum[4][maxn],ex,ey,n;
     string s;
     int judge(int x)
     {
         int l,r,u,d,cntx,cnty;
         if(x==n){
             int temp=x-abs(ex)-abs(ey);
             if(temp>=0&&temp%2==0)
                 return 1;
             return 0;
         }
         for(int i=1;i<=n-x+1;i++){
             l=sum[0][i-1]+(sum[0][n]-sum[0][i+x-1]);
             r=sum[1][i-1]+(sum[1][n]-sum[1][i+x-1]);
             u=sum[2][i-1]+(sum[2][n]-sum[2][i+x-1]);
             d=sum[3][i-1]+(sum[3][n]-sum[3][i+x-1]);
             cnty=u-d,cntx=r-l;
             int temp=x-(abs(ey-cnty)+abs(ex-cntx));
             if(temp>=0&&temp%2==0) return 1;
         }
        return 0;
     }
     int main()
     {
         scanf("%d",&n);
         cin>>s>>ex>>ey;
         int ret=-1,l=0,r=n;
         m['L']=0,m['R']=1,m['U']=2,m['D']=3;
         for(int i=1;i<=n;i++){
             for(int j=0;j<4;j++)
                 sum[j][i]=sum[j][i-1];
             sum[m[s[i-1]]][i]++;
         }
         while(l<=r){
             int mid=(l+r)>>1;
             if(judge(mid)) r=(ret=mid)-1;
             else l=mid+1;
         }
        cout<<ret<<endl;
     }
    View Code

    455A. Boredom(DP+map)

    题意:

    给一个数组,每次可以从数组中拿出一个数,但是每拿一次就得将所有x+1,x-1的数从数组中删除,问你能拿出数的最大和是多少

    思路:

    拿map记录下每个数出现的次数,然后对数组排序去重,之后开始DP

    DP为选了第i个数的最大值

    转移方程式为:dp[i]=max(dp[i],dp[i-1]+a[i]*s[a[i]]) (a[i]>a[i-1]+1)

           dp[i]=max(dp[i],mx+a[i]*s[a[i]]) (mx为dp[1]-dp[i-2]中的最大值)

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<map>
     using namespace std;
     typedef long long ll;
     const int maxn=1e5+10;
     map<ll,int> s;
     ll a[maxn],dp[maxn];
     int main()
     {
         int n;
         scanf("%d",&n);
         for(int i=1;i<=n;i++){
             scanf("%lld",&a[i]);
             s[a[i]]++;
         } 
         sort(a+1,a+1+n);
         int len=unique(a+1,a+1+n)-a-1;
        dp[1]=a[1]*s[a[1]];
        ll ans=dp[1],mx=0;
         for(int i=2;i<=len;i++){
             if(a[i]>a[i-1]+1) dp[i]=max(dp[i],dp[i-1]+a[i]*s[a[i]]);
             dp[i]=max(dp[i],mx+a[i]*s[a[i]]);
            mx=max(dp[i-1],mx);
             ans=max(ans,dp[i]);
         }
        cout<<ans<<endl;
        return 0;
     }
    View Code

    225C. Barcode(DP)

    题意:

    有一个矩阵,每个位置是#或.,现你可以改变矩阵中某些位置,使得矩阵满足每列都相同,同一种符号的列连续出现的次数cnr必须满足,x≤cnt≤y

    思路:

    先求出把1-i列全部变成#的前缀和,然后定义dp[i][j]为前1-i列,最后连续一段为j所需要的最小花费

    那么状态转移方程式为:f[i][0]=min(f[i][0],f[i-j][1]+(s[i]-s[i-j]))

               f[i][1]=min(f[i][1],f[i-j][0]+n*j-(s[i]-s[i-j]))

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int a[1005][1005];
    int s[1005];
    char c[1005];
    int f[1005][2];
    int main(){
        memset(f,7,sizeof(f));
        f[0][0]=f[0][1]=0;
        int n,m,x,y;
        scanf("%d%d%d%d",&n,&m,&x,&y);
        for(int i=1;i<=n;i++){
            scanf("%s",c+1);
            for(int j=1;j<=m;j++){
                a[i][j]=(c[j]=='#'?1:0);
            }
        }
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                s[i]+=a[j][i];
            }
            s[i]+=s[i-1];
        }
        for(int i=x;i<=m;i++){
            for(int j=x;j<=y;j++){
                if(i-j>=0){
                    f[i][0]=min(f[i][0],f[i-j][1]+(s[i]-s[i-j]));
                    f[i][1]=min(f[i][1],f[i-j][0]+n*j-(s[i]-s[i-j]));
                }
            }
        }
        printf("%d
    ", min(f[m][0],f[m][1]));
        return 0;
    }
    View Code
  • 相关阅读:
    Atlassian JIRA 系统服务部署(包括5.0.3版本和7.2版本)
    LoadRunner系列之—-01 接口压力测试脚本
    oracle 正则查询json返回报文中某个字段的值
    Selenium系列之--05 页面元素找不到的分析思路
    Selenium系列之--04 不同浏览器获取Xpath的方法
    Selenium系列之--01开篇
    【问题记录】LoadRunner 接口压测-json格式报文
    oracle随机数
    十 删除topic中的数据
    九 assign和subscribe
  • 原文地址:https://www.cnblogs.com/overrate-wsj/p/12373149.html
Copyright © 2011-2022 走看看