牛客动态规划1选做

[NOIP2002]过河卒

题目链接

进行记忆化搜索,然后强制把马所在的点和控制的点赋值为 0

#include<bits/stdc++.h>
#define int long long
using namespace std;

set<pair<int,int>> st;

int f[25][25];

int calc( int x , int y ){
    if( x == 0 && y == 0 ) return f[x][y] = 1;
    if( x < 0 || y < 0 ) return 0;
    if( st.count( {x,y} ) ) return 0;
    if( f[x][y] ) return f[x][y];
    return f[x][y] = calc( x - 1 , y ) + calc( x , y - 1 );
}

int32_t main() {
    int n , m , s , t;
    cin >> n >> m >> s >> t;
    st.insert( { s , t } );
    st.insert( { s-1 , t-2 } ) ,st.insert( { s-1 , t+2 } );
    st.insert( { s+1 , t-2 } ) ,st.insert( { s+1 , t+2 } );
    st.insert( { s-2 , t-1 } ) ,st.insert( { s-2 , t+1 } );
    st.insert( { s+2 , t-1 } ) ,st.insert( { s+2 , t+1 } );
    cout << calc( n , m ) << "\n";
    return 0;
}

[NOIP2008]传球游戏

题目链接

设状态f[i][j]表示第j次传球后,球在i手上的点方案数,则f[i][j]=f[i-1][j-1]+f[i+1][j-1]

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int  N = 35;
int f[N][N] , n , m;

int32_t main() {
    cin >> n >> m;
    f[1][0] = 1;
    for( int j = 1 ; j <= m ; j ++ ){
        f[1][j] = f[n][j-1] + f[2][j-1];
        f[n][j] = f[1][j-1] + f[n-1][j-1];
        for( int i = 2 ; i < n ; i ++ )
            f[i][j] = f[i-1][j-1] + f[i+1][j-1];
    }
    cout << f[1][m] << "\n";

    return 0;
}

滑雪

题目链接

记忆化搜索一下,f[x][y]表示从(x,y)出发最远可以走多远。

每次只能从相邻且比当前低的点转移过来

#include<bits/stdc++.h>

using namespace std;

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}


const int N = 105;
const int dx[] = { 0 , 0 , 1 , -1 } , dy[] = { 1 , -1 , 0 , 0 };
int n , m , h[N][N] , f[N][N] , res = INT_MIN;

int dfs( int x , int y ){
    if( x < 1 || y < 1 || x > n || y > m ) return 0;
    if( f[x][y] ) return f[x][y];
    f[x][y] = 1;
    for( int i = 0 , fx , fy ; i < 4 ; i ++ ){
        fx = x + dx[i] , fy = y + dy[i];
        if( h[x][y] > h[fx][fy] ) f[x][y] = max( f[x][y] , dfs( fx , fy ) + 1 );
    }
    return f[x][y];
}

int32_t main() {
    n = read() , m = read();
    for( int i = 1 ; i <= n ; i ++ )
        for( int j = 1 ; j <= m ; j ++ )
            h[i][j] = read();
    for( int i = 1 ; i <= n ; i ++ )
        for( int j = 1 ; j <= m ; j ++ )
            res = max( res , dfs( i , j ) );
    cout << res;
    return 0;
}

方块与收纳盒

https://ac.nowcoder.com/acm/contest/24213/1001

简单的斐波那契额数列

递推版

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

ll f[100];

int32_t main() {
    f[0] = f[1] = 1;
    for( int i = 2 ; i <= 80 ; i ++ ) f[i] = f[i-1] + f[i-2];
    for( int t = read() , x ; t ; t -- )
        x = read() , cout << f[x] << "\n";
    return 0;
}

递归版

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

ll f[100];

ll calc( int x ){
    if( f[x] ) return f[x];
    return f[x] = calc( x - 1 ) + calc( x - 2 );
}

int32_t main() {
    f[0] = f[1] = 1;
    for( int t = read() , x ; t ; t -- )
        x = read() , cout << calc(x) << "\n";
    return 0;
}

舔狗舔到最后一无所有

https://ac.nowcoder.com/acm/contest/24213/1002

f[i]表示前i天的合法方案。

如果i天与i-1天不同,则有两种选择方法

如果i天与i-1天相同,就不能与i-2天相同,与i-2天不同有两种选择方法

所以f[i] = f[i-1] * 2+ f[i-2]*2

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N = 1e5+5 , mod = 1e9+7;
int f[N];
int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

int32_t main() {
    f[1] = 3 , f[2] = 9;
    for( int i =3 ; i < N ; i ++ )
        f[i] = ( f[i-1] + f[i-2] ) % mod * 2 % mod;
    for( int t = read() , x ; t ; t -- )
        x = read() , cout << f[x] << "\n";
    return 0;
}

[NOIP2001]装箱问题

https://ac.nowcoder.com/acm/contest/24213/1017

f[i][j]表示前i个物品能否填满空间j这样就可以得到状态转移方程f[i][j] |= f[i-1][j-v]

然后可以通过倒序枚举的方式优化一维空间

#include <iostream>
#include <string>
#include <vector>
using namespace std;

const int N = 20005;
bool f[N];

int main(){
    int v , n; cin >> v >> n;
    f[0] = 1;
    for( int i = 1 , x ; i <= n ; i ++ ){
        cin >> x;
        for( int i = v ; i >= x ; i -- )
            f[i] |= f[i-x];
    }
    for( int i = v ; i >= 0 ; i -- )
        if( f[i] ) cout << v-i , exit(0);
    return 0;
}

可爱の星空

因为合并的时候肯定是尽可能接近的两个数合并才可以使话费尽可能的小,所以为了合成n必须先合成n/2,然后处理一下奇数的情况就好

#include<bits/stdc++.h>
#define int long long
using namespace std;

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

map< int , int > g;

int f( int x ) {
    if( x == 1 || x == 2 ) return 0;
    if( g[x] != 0 ) return g[x];
    return g[x] = f(x/2) + f( x/2 + (x&1)) + (x&1);
}

void solve(){
    int n = read();
    cout << f(n) << "\n";
}


int32_t main() {
    for( int t = read() ; t ; t -- )
        solve();
    return 0;
}

数字三角形

#include<bits/stdc++.h>
using namespace std;


int32_t main() {
    int n; cin >> n;
    for( int i = 1 , t = 1 ; i <= n ; i ++ ){
        for( int j = 1 ; j <= i ; j ++ , t ++ )
            cout << setw(4) << t;
        cout << "\n";
    }
    return 0;
}

[NOIP2005]采药

https://ac.nowcoder.com/acm/contest/24213/1018

#include <bits/stdc++.h>
using namespace std;

int main(){
	int n, m;
	cin >> n >> m;
	vector<int> f( n+1 , INT_MIN );
	f[0] = 0;
	for( int v , w ; m ; m -- ){
		cin >> v >> w;
		for( int i = n ; i >= v ; i -- )
			f[i] = max( f[i] , f[i-v] + w);
	}
	cout << *max_element(f.begin(), f.end());	
	return 0;
}

[NOIP2004]合唱队形

求两边最长上升子序列,枚举中间点

#include<bits/stdc++.h>

using namespace std;

#define int long long

int read() {
    int x = 0, f = 1, ch = getchar();
    while ((ch < '0' || ch > '9') && ch != '-') ch = getchar();
    if (ch == '-') f = -1, ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x * f;
}

int32_t main() {
    int n = read();
    vector<int> a(n+1) , f(n+1 , 1 ) , g(n+1 , 1 );
    for( int i = 1 ; i <= n ; i ++ ) a[i] = read();
    for( int i = 1 ; i <= n ; i ++ )
        for( int j = 1 ; j < i ; j ++ )
            if( a[j] < a[i] ) f[i] = max( f[i] , f[j] + 1 );
    for( int i = n ; i >= 1 ; i -- )
        for( int j = n ; j > i ; j -- )
            if( a[j] < a[i] ) g[i] = max( g[i] , g[j] + 1 );
    int res = INT_MIN;
    for( int i = 1 ; i <= n ; i ++ )
        res = max( res , f[i] + g[i] - 1);
    cout << n - res << "\n";
    return 0;
}
posted @ 2023-04-19 19:12  PHarr  阅读(13)  评论(0编辑  收藏  举报