8.6 edu25 ,577#div2 CF补题(二分 ,dp 与 贪心
两道题
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的转移状态有四个:
根据当前的落脚位置判断(l,r)转移方向,走的梯子肯定是(当前或下一排的l)左右最靠近的梯子 或(当前或下一排的r)左右最靠近的梯子(贪心思想)
3.赛后再写发现不太对,因为这样转移水平方向会来回重复走
应该是:
这样每一排的状态就能相连成从上到下的最短路径
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还是思维有缺陷,再接再厉,再接再厉