• 四边形优化DP(POJ_1160 && HDU_2829 && HDU_3480 && HDU_3506 && HDU_3516)


    这几天补了一下四边形优化DP。。。(证明的论文也就是假装看看),下面来总结一下:

    我们一般列出DP转移方程:dp[i][j] = min(dp[i][k] + dp[k][j] + w[i][j]) 这种形式的时候,如果对于i < i' < j < j' : w[i'][j] <= w[i][j'] 并且 w[i][j] + w[i'][j'] <= w[i'][j] + w[i][j'] 那么我们称w满足四边形不等式。由此,我们有定理1 :dp[i][j]也满足四边形不等式。定理2:s[i][j](对于dp[i][j]的最优决策) ,满足: i < j : s[i-1][j] <= s[i][j] <= s[i][j+1] , s[i][j-1] <= s[i][j] <= s[i+1][j] (这两个可以互推,为什么要加入后面这个,因为做了5题下来,感觉四边形优化dp还是分成两类的)。

    为什么说分成两类:一类的递推式是从大区间->小区间进行递推(不是区间DP),比如前三道题。用第一个不等式

         第二类递推式就是区间DP,小区间->大区间。这种时候用第二个不等式。网上好多人都是直接从长度入手写的(果然可能我的思维比较奇葩一点,反正我这样写自己感觉自然一点)。

    第一类写法是:i从小到大,j从大到小。第二类写法反过来:i从大到小,j从小到大,这个应该都能理解。

    然后我觉得四边形优化DP还有一点比较重要的就是边界条件(等到做的题目多了,再回来总结,现在感觉就是有个笼统的边界条件+每一题的特定的边界条件)。

    虽然感觉四边形优化DP(毕竟对于斜率优化DP来说)理解起来比较麻烦,证明起来更麻烦,其实写起来还是蛮容易的。至于有的时候你实在可能想不出来w[i][j]是否满足条件,这个时候看数据范围,o(n^2)过,o(n^3)过不了的基本就差不多用吧。

    下面来说题,贴代码(先简写一下,毕竟作业还没写完,实验报告还没抄完,等过几天再来补吧):

    POJ_1160:

    dp[i][j] = min(dp[i-1][k] + w[k+1][j]):
    #include <iostream>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <cstdio>
    #include <queue>
    #define INF 0x7ffffff
    #define ll  long long
    #define FOR(i,x,y)  for(int i = x;i < y;i ++)
    
    using namespace std;
    
    int v[333],P,V;
    int dp[33][333];
    int s[33][333];
    int w[333][333];
    
    int main()
    {
        //freopen("test.in","r",stdin);
        while(~scanf("%d%d",&V,&P)){
            FOR(i,1,V+1){
                scanf("%d",&v[i]);
            }
            FOR(i,1,V+1){
                w[i][i] = 0;
                FOR(j,i+1,V+1){
                    w[i][j] = w[i][j-1] +v[j] - v[(i+j)>>1];
                }
            }
            FOR(i,1,P+1){
                FOR(j,1,V+1){
                    dp[i][j] = INF;
                }
            }
            FOR(i,1,V+1){
                dp[1][i] = w[1][i];
                s[1][i] = 0;
            }
            FOR(i,2,P+1){
                s[i][V+1] = V;
                for(int j = V;j >= i;j --){
                    FOR(k,s[i-1][j],s[i][j+1]+1){
                        if(dp[i][j] > dp[i-1][k] + w[k+1][j]){
                            dp[i][j] = dp[i-1][k] + w[k+1][j];
                            s[i][j] = k;
                        }
                    }
                }
            }
            printf("%d
    ",dp[P][V]);
        }
        return 0;
    }
    HDU_2829:
    dp[i][j] = dp[i-1][k] + w[k+1][j];
    #include <iostream>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #include <cstdio>
    #include <queue>
    #define INF (ll)1<<60
    #define ll  long long
    #define FOR(i,x,y)  for(int i = x;i < y;i ++)
    
    using namespace std;
    
    ll v[1111],P,V;
    ll dp[1111][1111];
    ll s[1111][1111];
    ll w[1111][1111];
    ll sum[1111];
    
    int main()
    {
        //freopen("test.in","r",stdin);
        while(~scanf("%I64d%I64d",&V,&P) && (V || P)){
            FOR(i,1,V+1){
                scanf("%I64d",&v[i]);
            }
            sum[0] = 0;
            FOR(i,1,V+1){
                sum[i] = sum[i-1]+v[i];
            }
            FOR(i,1,V+1){
                w[i][i] = 0;
                FOR(j,i+1,V+1){
                    w[i][j] = w[i][j-1] + v[j]*(sum[j-1] - sum[i-1]);
                }
            }
            FOR(i,1,P+2){
                FOR(j,i,V+1){
                    dp[i][j] = INF;
                }
            }
            FOR(i,1,V+1){
                dp[1][i] = w[1][i];
                s[1][i] = 0;
            }
            FOR(i,2,P+2){
                s[i][V+1] = V;
                for(int j = V;j >= i;j --){
                    for(int k = s[i-1][j];k <= s[i][j+1];k ++){
                        if(dp[i][j] > dp[i-1][k] + w[k+1][j]){
                            dp[i][j] = dp[i-1][k] + w[k+1][j];
                            s[i][j] = k;
                        }
                    }
                }
            }
            printf("%I64d
    ",dp[P+1][V]);
        }
        return 0;
    }
    
    
    HDU_3480:
    这道题有一个问题就是内存的问题,其实也不麻烦,不要保存w[i][j]就行了
    dp[i][j] = dp[i-1][k] + w;
    w = (num[j] - num[k+1]) * (num[j] - num[k+1]);
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #define FOR(i,x,y)  for(int i = x;i < y;i ++)
    #define IFOR(i,x,y) for(int i = x;i > y;i --)
    #define ll  long long
    #define INF 0x7ffffff
    
    using namespace std;
    
    const int MAXN = 11111;
    int num[MAXN];
    int dp[MAXN>>1][MAXN];
    int s[MAXN>>1][MAXN];
    int n,m;
    
    int main()
    {
        //freopen("test.in","r",stdin);
        int t,tCase=0;
        scanf("%d",&t);
        while(t--){
            scanf("%d%d",&n,&m);
            FOR(i,1,n+1)    scanf("%d",&num[i]);
            sort(num+1,num+n+1);
            FOR(i,1,m+1){
                FOR(j,1,n+1){
                    dp[i][j] = INF;
                }
            }
            FOR(i,1,n+1){
                s[1][i] = 0;
                dp[1][i] = (num[i] - num[1]) * (num[i] - num[1]);
            }
            FOR(i,2,m+1){
                s[i][n+1] = n;
                IFOR(j,n,i-1){
                    FOR(k,s[i-1][j],s[i][j+1]+1){
                        int w = (num[j] - num[k+1]) * (num[j] - num[k+1]);
                        if(dp[i][j] > dp[i-1][k] + w){
                            dp[i][j] = dp[i-1][k] + w;
                            s[i][j] = k;
                        }
                    }
                }
            }
            printf("Case %d: ",++tCase);
            printf("%d
    ",dp[m][n]);
        }
        return 0;
    }

    HDU_3506
    这道题目还是蛮有意思的,是一个环形的,其实只需要再接一块就好了,然而我已开始很蠢的把接的一块写成了对称过去的了(唉。。。)
    dp[i][j] = dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1];

    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #define FOR(i,x,y)  for(int i = x;i < y;i ++)
    #define IFOR(i,x,y) for(int i = x;i > y;i --)
    #define ll long long
    #define INF (ll)1<<60
    
    using namespace std;
    
    const int MAXN = 1111;
    ll dp[MAXN<<1][MAXN<<1];
    int num[MAXN<<1];
    int sum[MAXN<<1];
    int n,s[MAXN<<1][MAXN<<1];
    
    int main(){
        //freopen("test.in","r",stdin);
        while(~scanf("%d",&n)){
            FOR(i,1,n+1)    scanf("%d",&num[i]),num[n+i] = num[i];
            sum[0] = 0;
            FOR(i,1,2*n)    sum[i] = sum[i-1] + num[i];
            FOR(i,1,n+1){
                FOR(j,1,i){
                    dp[i][j] = 0;
                }
                FOR(j,i,2*n){
                    dp[i][j] = INF;
                }
            }
            FOR(i,1,n+2){
                dp[i][i] = 0;
                s[i][i] = i;
                s[n+1][i] = i;
            }
            ll ans = INF;
            IFOR(i,n,0){
                FOR(j,i+1,2*n){
                    FOR(k,s[i][j-1],s[i+1][j]+1){
                        if(dp[i][j] > dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]){
                            dp[i][j] = dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1];
                            s[i][j] = k;
                        }
                    }
                }
                ans = min(ans,dp[i][n+i-1]);
            }
            printf("%I64d
    ",ans);
        }
        return 0;
    }

    HDU_3516:
    这道题写了好长时间,其实还是题做的少吧,一开始没看出来是一个石子合并的问题(真是蠢啊),然后之前的DP方程列出来直接上了四边形优化,然后发现四边形优化的条件是很苛刻的(dp[i][i] == 0),这个条件一开始就不是很在意,写这道题的时候真是蠢了,然后回头又看了一下证明(还是需要的)。
    tem = dp[i][k] + dp[k+1][j] + y[k] - y[j] + x[k+1] - x[i];
    dp[i][j] = min{tem};
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #define FOR(i,x,y)  for(int i = x;i < y;i ++)
    #define IFOR(i,x,y) for(int i = x;i > y;i --)
    #define ll  long long
    #define INF (ll)1<<60
    
    using namespace std;
    
    const int MAXN = 1111;
    int n;
    int x[MAXN],y[MAXN];
    int s[MAXN][MAXN];
    ll dp[MAXN][MAXN];
    
    int main(){
        //freopen("test.in","r",stdin);
        while(~scanf("%d",&n)){
            FOR(i,1,n+1)    scanf("%d%d",&x[i],&y[i]);
            FOR(i,1,n+1){
                FOR(j,i,n+1){
                    dp[i][j] = 0;
                    s[i][j] = 0;
                }
            }
            FOR(i,1,n+1){
                dp[i][i] = 0;
                dp[n+1][i] = INF;
                s[i][i] = i;
                FOR(j,i+1,n+1){
                    dp[i][j] = INF;
                }
            }
            IFOR(i,n,0){
                FOR(j,i+1,n+1){
                    FOR(k,s[i][j-1],s[i+1][j]+1){
                        ll tem = dp[i][k] + dp[k+1][j] + y[k] - y[j] + x[k+1] - x[i];
                        if(dp[i][j] > tem){
                            dp[i][j] = tem;
                            s[i][j] = k;
                        }
                    }
                }
            }
            printf("%I64d
    ",dp[1][n]);
        }
        return 0;
    }




  • 相关阅读:
    【MVC 4】7.SportsSore:完成购物车
    【MVC 4】6.SportsSore:导航
    【MVC 4】5.SportsSore —— 一个真实的应用程序
    【网络文摘】面试感悟:3年工作经验程序员应有的技能
    【网络文摘】一个大神程序员的使命感究竟应该是什么
    join的简单总结
    模块化(1):基本思路
    Android 9.0新特性
    DataBinding初认识
    Android 7.0 新特性
  • 原文地址:https://www.cnblogs.com/hqwhqwhq/p/4555871.html
Copyright © 2020-2023  润新知