2-DP_题解

2-DP_题解

比赛链接

A,不要62_前缀和

题目大意

中文题

思路和代码

初始化一个前缀和数组表示[1,i]之间有多少有效车牌即可

ll l , r ;

ll p[N] ;

ll ck(ll x){
	ll y = x ;
	while(y){
		int t = y % 10 ;
		if(t == 4) return 0 ;
		if(t == 2){
			t = y / 10 % 10 * 10 + t ;
			if(t == 62) return 0 ;
		}
		y /= 10 ;
	}
	return 1 ;
}

int main(){
	
//	freopen("in.in" , "r" , stdin) ;
	ios::sync_with_stdio(false) ;
	cin.tie(0) ; cout.tie(0) ;

	rep(i , 1 , 1e6 + 10){
		p[i] = p[i - 1] + ck(i) ;
	}
	
	while(1){
		cin >> l >> r ;
		if(!l && !r) break ;
		cout << p[r] - p[l - 1] << "\n" ;
	}
	
}//code_by_tyrii 

B,Constanze's Machine

题目大意

给一个串,其中不能出现w或m。w被uu代替,m被nn代替。现在问该串最多有几种含义。

思路和代码

方法一:

以“uuuuuu”举例,下图每个圈都是一个u,画红线表示uu组合成w。设fi为长度为i的全u或全v的种数。

可以发现长度为k的全u或者全v可以由以下式子算出答案:

所以双指针找出所有全u或全v的区间,利用fi即可得到答案。

int n , m , k ; 

bool ck(ll x , string &s){
	if((s[x] != 'u' && s[x] != 'n') || x == n) return 0 ;
	if(s[x] != s[x + 1]) return 0 ;
	return 1 ;
}

ll f[N] ;

void solve(){
	
	
	string s ;
	cin >> s ;
	
	s = " " + s ;

	ll ans = 1 ;
	
	n = s.size() - 1 ;
	f[0] = f[1] = 1 ;
	f[2] = 2 ;
	f[3] = 3 ;
	ll sum = 4 ;
	rep(i , 4 , n){
		sum = (sum + 1) % mod ;
		f[i] = sum ;
		sum = (sum + f[i - 1]) % mod ;
	}
	
	rep(i , 1 , n) cout << f[i] << " \n"[i == n] ;
	
	rep(i , 1 , n)
	if(s[i] == 'm' || s[i] == 'w'){
		cout << "0\n" ; return ;
	}
	
	ll l = 1 , r = 1 ;
	while(l <= n && r <= n){
		while(l <= n && !ck(l , s)) l ++ ;
//		cout << l << "\n" ;
		if(l > n) break ;
		r = l + 1 ;
		while(r <= n && s[r] == s[l]) r ++ ;
//		cout << l << " " << r << "\n" ;
		ans = (ans * f[r - l]) % mod ;
		l = r ;
	}
	cout << ans << "\n" ;
}//code_by_tyrii 

法二:

对于长度为L的全u或全v串来说,如果再加上一个一样的字符,则会变成以下情况:

所以之前代码的f初始化就可以直接改为fibonacci数列。(代码略)

C,Bellovin_LIS

题目大意

给一个数列a,F(a)i 表示以ai结尾的LIS(最长上升子序列)的长度。现在要找到字典序最小的数列b,使得F(a)=F(b)

思路和代码

这题是LIS模板,代码建议保存。

设置ans数组,ansi表示以ai结尾的LIS长度。

可以发现最优的数列b就是ans数组。因为ans1一定是1,整个数列的字典序一定是最小的。所以题目就转化为以O(nlogn)复杂度求LIS。

下面给出模板。

void solve(){
	cin >> n ;
	
	vct<ll> dp(n + 1 , INF) ;
	vct<ll> ans(n + 1 , 0) ;
	
	rep(i , 1 , n){
		ll t ; cin >> t ;
		ll idx = lower_bound(all(dp) , t) - dp.begin() ;
		dp[idx] = t ;
		ans[i] = idx + 1 ;
	}
	rep(i , 1 , n) cout << ans[i] << " \n"[i == n] ;
}//code_by_tyrii 

D,最长公共子序列_LIS

题目大意

中文

思路和代码

正常做LCS问题,需要n2的dp完成,但是这题数据范围1e5,就不能用n2的方法。题中给了一个条件,n个数字是n的排列。这一限制就可以用另外一种思路。

我们只要设置idx数组,表示x在a数组中的位置,然后对idx[bi]求LIS即可。这道题目比较巧妙的将LCS问题转化成了LIS问题。

void solve(){
	cin >> n ;
	
	vct<ll> idx(n + 1 , 0 ) ;
	rep(i , 1 , n){
		ll t ; cin >> t ;
		idx[t] = i ;
	}
	
	vct<ll> dp(n + 1 , INF) ;
	vct<ll> ans(n + 1 , 0) ;
	
	rep(i , 1 , n){
		ll t ; cin >> t ;
		ll j = lower_bound(all(dp) , idx[t]) - dp.begin() ;
		dp[j] = idx[t] ;
		ans[i] = j + 1 ;
	}
//	rep(i , 1 , n) cout << ans[i] << " \n"[i == n] ;
	cout << *max_element(all(ans)) << "\n" ;
}//code_by_tyrii  

D,导弹拦截_贪心

题目大意

中文题

思路和代码

这道题就是要把所有的导弹分成两堆,一堆用第一个装置来拦截,另一堆用第二个装置来拦截。而拦截导弹具有单调性,拦截了远的导弹就可以把近的也一起拦截掉。所以用到第一个装置的距离降序排序,[i,n]部分由装置1拦截,[1,i-1]部分由装置2拦截即可。

void solve(){
	ll x1 , x2 , y1 , y2 , n ;
	cin >> x1 >> y1 >> x2 >> y2 >> n ;
	vct<missile> a(n + 1) ;
	rep(i , 1 , n){
		ll tx , ty ;
		cin >> tx >> ty ;
		a[i].to1 = getdis(x1 , y1 , tx , ty) ;
		a[i].to2 = getdis(x2 , y2 , tx , ty) ;

	}
	sort(a.begin() + 1 , a.end() , cmp) ;
	
    ll ans = INF ;
	ll m = -INF ;
	ans = a[1].to1 ;
	rep(i , 1 , n - 1){
		m = max(a[i].to2 , m) ;
		ans = min(ans , a[i+1].to1+m ) ;
	}
	cout << ans << "\n" ;
}//code_by_tyrii 

G,石子合并

题目大意

中文

思路和代码

入门区间dp,不过是在一个环上进行的。把环断开成链即可,这里不多赘述。

ll n , m , k ; 

ll a[N] ;
ll p[N] ;

ll ansmin = INF ;
ll ansmax = -INF ;

void DP(ll a[]){
	ll dpmax[N][N] = {} ;
	ll dpmin[N][N] = {} ;
	
	rep(i , 1 , n)
	rep(j , i , n) dpmin[i][j] = INF ;
	
	rep(i , 1 , n) dpmin[i][i] = 0 ;
	
	rep(i , 1 , n) p[i] = p[i - 1] + a[i] ;
	
	rep(L , 1 , n)
	for(int i = 1 ; i + L - 1 <= n ; i ++ ){
		int j = i + L - 1 ;
		rep(k , i , j - 1){
			dpmax[i][j] = max(dpmax[i][j] , dpmax[i][k] + dpmax[k + 1][j] + p[j] - p[i - 1]) ;
			dpmin[i][j] = min(dpmin[i][j] , dpmin[i][k] + dpmin[k + 1][j] + p[j] - p[i - 1]) ;
		}
	}
	ansmax = max(ansmax , dpmax[1][n]) ;
	ansmin = min(ansmin , dpmin[1][n]) ;
}

void solve(){
	cin >> n ;

	rep(i , 1 , n) cin >> a[i] ;
	
	rep(j , n + 1 , 2 * n) a[j] = a[j - n] ;
	
	rep(i , 1 , n){
		ll b[N] = {} ;
		rep(j , i , i + n - 1) b[j - i + 1] = a[j] ;
		DP(b) ;
	}
	cout << ansmin << "\n" << ansmax << "\n" ;
}//code_by_tyrii 
posted @ 2022-07-07 19:03  tyrii  阅读(56)  评论(0)    收藏  举报