zoukankan      html  css  js  c++  java
  • dp练习

    练习1 CF 1132F Clear the String

    大意: 给定字符串, 每次消除同色连通块, 求最少多少次消除完

    考虑区间DP, 只考虑从左侧消除的情况, 因为从右侧转移到左侧与从左侧转移到右侧是等价的, 复杂度O(n^3)

    const int N = 600;
    int n;
    char s[N];
    int dp[N][N];
    
    int solve(int l, int r) {
    	if (l>r) return 0;
    	int &ans = dp[l][r];
    	if (ans) return ans;
    	ans = 1+solve(l+1,r);
    	REP(i,l+1,r) if (s[i]==s[l]) ans=min(ans,solve(l+1,i)+solve(i+1,r));
    	return ans;
    }
    
    
    int main() {
    	scanf("%d%s", &n, s+1);
    	printf("%d
    ",solve(1,n));
    }
    

    练习2 CF 1107E Vasya and Binary String

    大意: 给定01字符串, 每次消除同色连通块, 得分为$w_len$, $len$为消除的长度, 求最大得分

    设$dp[l][r][pre]$为$l$前有$pre$个与$l$同色的最大得分, 同上题转移就好了

    ll dfs(int l, int r, int pre) {
    	if (l>r) return 0;
    	ll &ans = dp[l][r][pre];
    	if (ans) return ans;
    	if (l==r) return ans=a[pre];
    	ans = a[pre]+dfs(l+1,r,1);
    	REP(i,l+1,r) if (s[i]==s[l]) ans=max(ans,dfs(l+1,i-1,1)+dfs(i,r,pre+1));
    	return ans;
    }
    
    int main() {
    	scanf("%d%s", &n, s+1);
    	REP(i,1,n) scanf("%d", a+i);
    	printf("%lld
    ", dfs(1,n,1));
    }
    

    练习3. CF 1107F Vasya and Endless Credits

    考虑最优解的结构, 一定是有一部分全部还完, 还有一部分没有还完, 还完的直接在最开始买就行, 对于未还完的, 购买的时间一定连续, 并且$b_i$是非增的, 所以可以按照$b_i$排序转移即可, 复杂度$O(n^2)$

    const int N = 1e3+10;
    int n;
    struct _ {
    	int a, b, k;
    } a[N];
    ll dp[N];
    
    
    int main() {
    	scanf("%d", &n);
    	ll ans = 0;
    	REP(i,1,n) {
    		scanf("%d%d%d", &a[i].a, &a[i].b, &a[i].k);
    	}
    	sort(a+1,a+1+n,[](_ a,_ b){return a.b>b.b;});
    	REP(i,1,n) {
    		PER(j,0,n-1) {
    			dp[j+1]=max(dp[j+1],dp[j]+a[i].a-(ll)j*a[i].b);
    			dp[j]=max(dp[j],dp[j]+a[i].a-(ll)a[i].k*a[i].b);
    		}
    	}
    	printf("%lld
    ", *max_element(dp,dp+1+n));
    }
    

    练习4 CF 1114D Flood Fill 

    大意: n个带颜色的格子, 初始选择一个格子$x$, 每次操作改变$x$的颜色, $x$的连通块颜色同时改变, 求最少操作数使得格子全部同色

    贪心尽量让左右两侧的同色一起连通, 相当于对同色的连边, 且不能交叉, 求最大连边数. 这个可以用区间dp求出, 再用总的连通块减去边数就行, 转移二分了一下贪心找尽量靠右的点与左端点匹配, 复杂度$O(n^2logn)$, 其实可以预处理一下达到O(n^2)懒得写了..

    int dfs(int l, int r) {
        if (l>=r) return 0;
        if (vis[l][r]) return dp[l][r];
        vis[l][r]=1;
        int &ans = dp[l][r];
        ans = dfs(l+1,r);
        auto t = upper_bound(g[a[l]].begin(),g[a[l]].end(),r);
        --t;
        if (*t>r||*t<=l) return ans;
        return ans=max(ans,dfs(l+1,*t-1)+1);
    }
    
    int main() {
        scanf("%d", &n);
        REP(i,1,n) {
            scanf("%d", a+i);
            if (a[i]==a[i-1]) --i,--n;
        }   
        REP(i,1,n) g[a[i]].push_back(i);
        int mx = dfs(1,n);
        printf("%d
    ", n-mx-1);
    }

    这题看官方题解还有两种做法

    1. 设$dp[l][r][0/1]$为[l,r]区间最后颜色为s[l]或s[r]的最小值, 很容易实现O(n^2)

    2. 转为反串LCS

     

  • 相关阅读:
    《张艺谋这个人》较真
    《智能》是真智能
    《解密小米之互联网下的商业奇迹》
    《三毛。。。。》烂漫
    《盛典―― 诺奖之行》
    常用iOS、Mac框架和库及常用中文开发博客
    《人脸识别与人体动作识别技术及应用》
    《程序员第二步从程序员到项目经理》
    《信息安全导论》
    [leetCode]141.环形链表
  • 原文地址:https://www.cnblogs.com/uid001/p/10504202.html
Copyright © 2011-2022 走看看