8.6 edu25 ,577#div2 CF补题(二分 ,dp 与 贪心

时间:2021-10-30 20:56:05

两道题

1.edu 25 D. Suitable Replacement

题意:给定字符串s,t,s中‘?’字符可以以任何字符替换,问如何替换 可使 替换后的s重新排序与t的匹配次数最多(len_t<len_s)

分析:

1.比赛时又用贪心模拟结果把自己写死了啊啊啊

2.一些贪心的题可以用二分做,而且如果二分的check()简单的话比贪心好写

3.如果一些题感觉贪心模拟过程很复杂,就尝试用二分

4.这题匹配次数是很经典的二分用法,居然没想到,唉,再接再厉

    #include <bits/stdc++.h>
     
    #define fi first
    #define se second
    #define rep( i ,x ,y ) for( int i= x; i<= y ;i++ )
    #define reb( i ,y ,x ) for( int i= y; i>= x ;i-- )
    #define mem( a ,x ) memset( a ,x ,sizeof(a))
    using namespace std;
     
    typedef long long ll;
    typedef pair<int ,int> pii;
    typedef pair<ll ,ll> pll;
    typedef pair<string ,int> psi;
     
    const int N = 200005;
    const int inf = 0x3f3f3f3f;
     
    ll mp[5000] ,tmp[5000];
    string s ,t;
     
   // check()匹配次数,是很经典的二分题了
    bool check( ll x ){
        ll tm = mp['?'];
        rep( i ,'a' ,'z' ){
            if( x*tmp[i] > mp[i])
            tm -= x*tmp[i] - mp[i];
            //cout<<"x "<<x<< " i "<<(char)i<< "tm "<<tm<<endl;
            if( tm <0 )return false;
        }
        return true;
    }
     
    int main( ){
        cin >>s>>t;
        
        mem( mp ,0 ); mem( tmp ,0 );
        int ls = s.size() ,lt = t.size( );
        
        rep( i ,0 ,ls-1 )mp[s[i]]++;
        rep( i ,0 ,lt-1 )tmp[t[i]]++;
        
        if( mp['?']==0 )return (cout<<s), 0;
        
        ll l = 0 ,r = 1000001 ,ans = 0;
        while( l<=r ){
            ll mid = ( l+r ) >>1;
            if( check(mid) ) l = mid+1 ,ans = mid;
            else r = mid-1;
        }
        //cout<<ans<<endl;
        rep( i ,'a' ,'z'){
            mp[i] = ans*tmp[i] - mp[i];
        }
        rep( i ,0 ,ls-1 ){
            if( s[i]=='?' ){
                int flag = 1;
                rep( j ,'a' ,'z'){
                   if( mp[j] >0) {
                       cout<<(char)j;
                       mp[j]--; 
                       flag = 0;
                       break;
                   }
                }
                if( flag )cout<<'a';
            }
            else cout<<s[i];
        }
        return 0;
    }

 

2. div2 #577 D.Treasure Hunting

题意:给定一张地图,一些坐标位置有宝藏,只有部分竖列有*可以向下移动(不能向上),任何位置都可以左右移动,问寻完所有宝藏的最短步数

 

1.比赛时我想的是dp ,但当时就自暴自弃了,因为我知道写出来代码会很长,比赛结束前我调试不完

2.因为只有一个方向(下)的移动 ,每一排最左右的两个点是落脚位置(其他的点都会经过),走到每一排的最短步数是最优子结构且无后效性,所以可以dp

当时我想的dp的转移状态有四个:

8.6 edu25 ,577#div2 CF补题(二分 ,dp 与 贪心

根据当前的落脚位置判断(l,r)转移方向,走的*肯定是(当前或下一排的l)左右最靠近的* 或(当前或下一排的r)左右最靠近的*(贪心思想)

3.赛后再写发现不太对,因为这样转移水平方向会来回重复走

应该是:

8.6 edu25 ,577#div2 CF补题(二分 ,dp 与 贪心

这样每一排的状态就能相连成从上到下的最短路径

4.然后因为初始状态和边界又调试了很久orz

dp时状态和转移方程确定以后要特别注意初始状态

否则往往功亏一篑

#include <bits/stdc++.h>
 
#define fi first
#define se second
#define rep( i ,x ,y ) for( int i= x; i<= y ;i++ )
#define reb( i ,y ,x ) for( int i= y; i>= x ;i-- )
#define mem( a ,x ) memset( a ,x ,sizeof(a))
using namespace std;
 
typedef long long ll;
typedef pair<int ,int> pii;
typedef pair<ll ,ll> pll;
typedef pair<string ,int> psi;
 
const int N = 200005;
const int inf = 0x3f3f3f3f;
 
 
int n ,m ,k ,q;
ll li[N] ,ri[N] ,way[N] ,dl[2][N] ,dr[2][N] ,dp[N][5];
ll res ,id[N];
int main( ){
    res = 0;
    mem( li ,inf );
    mem( ri ,-1 );
    mem( dl ,inf );
    mem( dr ,inf );
    mem( dp ,inf );
    
    scanf("%d%d%d%d" ,&n ,&m ,&k ,&q );
    ll tmpl ,tmp;
    li[1] = ri[1] = 1;
    rep( i ,1 ,k ){
        scanf("%lld%lld" ,&tmpl ,&tmp );
        li[tmpl] = min( li[tmpl] ,tmp );
        ri[tmpl] = max( ri[tmpl] ,tmp );
    }
    rep( i ,1 ,q ){
        scanf("%lld" ,&way[i]);
    }
    sort( way+1 ,way+1+q );
    
    int pos1 ,pos2;
    //找靠近左右最近的* 
    rep( i ,1 ,n ){
        if( ri[i] == -1 )continue;
        pos1 = upper_bound( way+1 ,way +1+q ,ri[i] )-way;
        pos2 = pos1 - 1;
        // error --2
        if( pos1 != q+1 ) dr[0][i] = way[pos1];
        if( pos2 != 0 ) dr[1][i] = way[pos2];
        
        pos1 = upper_bound( way+1 ,way +1+q ,li[i] )-way;
        pos2 = pos1 - 1;
        if( pos1 != q+1 ) dl[0][i] = way[pos1];
        if( pos2 != 0 ) dl[1][i] = way[pos2];
    }
    
    ll cnt = 0; tmp = -1;
    rep( i ,1 ,n ){
        if( ri[i] == -1 )tmp++;
        else id[ ++cnt ] = i ,res += ++tmp ,tmp = 0;
    }
    
    //cout<< "dp 1 -- z 2 -- ] 3 -- C 4 -- rz "<<endl;
    dp[0][1] = dp[0][3] = abs( li[1] - ri[1]);
    // 初始状态又搞错了 ,初始状态必须从li[1] 开始 
    rep( i ,1 ,cnt-1 ){ 
        ll tmp1 ,tmp2 ,tmp3 ,tmp4 ,j = id[i] ,k = id[i+1];
        dp[i][1] =  min(dp[i-1][1] ,dp[i-1][3]) +  abs( li[k] - ri[k]);
        //cout<<"j ,k "<<j<<" "<<k<<endl;
        //cout<<"dr dl j "<<dr[0][j]<<" "<<dr[1][j]<<" "<<dl[0][j]<<" "<<dl[1][j]<<endl;
        //cout<<"dr dl k "<<dr[0][k]<<" "<<dr[1][k]<<" "<<dl[0][k]<<" "<<dl[1][k]<<endl;
        //cout<<"ri li j "<<ri[j]<<" "<<li[j]<<endl;
        //cout<<"ri li k "<<ri[k]<<" "<<li[k]<<endl;
        tmp1 =abs( ri[ j ] - dr[0][j] ) + abs( li[ k ] - dr[0][j] );
        tmp2 =abs( ri[ j ] - dr[1][j] ) + abs( li[ k ] - dr[1][j] );
        tmp3 =abs( ri[ j ] - dl[0][k] ) + abs( li[ k ] - dl[0][k] );
        tmp4 =abs( ri[ j ] - dl[1][k] ) + abs( li[ k ] - dl[1][k] );
        dp[i][1] += min( min(tmp1 ,tmp2) ,min(tmp3,tmp4));
        
        dp[i][2] =  min(dp[i-1][1] ,dp[i-1][3]) +  abs( li[k] - ri[k]);
        tmp1 =abs( ri[ j ] - dr[0][j] ) + abs( ri[ k ] - dr[0][j] );
        tmp2 =abs( ri[ j ] - dr[1][j] ) + abs( ri[ k ] - dr[1][j] );
        tmp3 =abs( ri[ j ] - dr[0][k] ) + abs( ri[ k ] - dr[0][k] );
        tmp4 =abs( ri[ j ] - dr[1][k] ) + abs( ri[ k ] - dr[1][k] );
        dp[i][2] += min( min(tmp1 ,tmp2) ,min(tmp3,tmp4));
        
        dp[i][3] =  min(dp[i-1][2] ,dp[i-1][4]) + abs( li[k] - ri[k]);
        tmp1 =abs( li[ j ] - dl[0][j] ) + abs( li[ k ] - dl[0][j] );
        tmp2 =abs( li[ j ] - dl[1][j] ) + abs( li[ k ] - dl[1][j] );
        tmp3 =abs( li[ j ] - dl[0][k] ) + abs( li[ k ] - dl[0][k] );
        tmp4 =abs( li[ j ] - dl[1][k] ) + abs( li[ k ] - dl[1][k] );
        dp[i][3] += min( min(tmp1 ,tmp2) ,min(tmp3,tmp4));
        
        dp[i][4] =  min(dp[i-1][2] ,dp[i-1][4]) + abs( li[k] - ri[k]);
        tmp1 =abs( li[ j ] - dl[0][j] ) + abs( ri[ k ] - dl[0][j] );
        tmp2 =abs( li[ j ] - dl[1][j] ) + abs( ri[ k ] - dl[1][j] );
        tmp3 =abs( li[ j ] - dr[0][k] ) + abs( ri[ k ] - dr[0][k] );
        tmp4 =abs( li[ j ] - dr[1][k] ) + abs( ri[ k ] - dr[1][k] );
        dp[i][4] += min( min(tmp1 ,tmp2) ,min(tmp3,tmp4));
        
        //cout<<"dp "<<i <<" "<< dp[i][1]<<" "<<dp[i][2]<<" "<<dp[i][3] <<" "<<dp[i][4]<<endl;
    }
    if(cnt == 1)printf("%lld" ,ri[1]-li[1]); 
    else printf( "%lld" ,res+min( min(dp[cnt-1][1] ,dp[cnt-1][2]) ,min(dp[cnt-1][3] ,dp[cnt-1][4])) ) ;
    // find shortest way 
    
    return 0;
}

5.然鹅这道题的题解是用贪心做的

贪心的过滤可以只保留上图2,3两种转移方式(其他的一定不如这两种步数少),然后就只有唯一一条转移路径(2,3交替),递推到最后就是答案

orz还是思维有缺陷,再接再厉,再接再厉