• 动态规划总结


    //动态规划
    //1.经典模型:数字三角形
    
    //*记忆化搜索
    int dfs(int y,int x){
        if(y==n) return value[y][x];
        if(dp[y][x] != -1) return dp[y][x];
        dp[y][x] = value[y][x] + max(dfs(y+1,x),dfs(y+1,x+1));
        return dp[y][x];
    }
    
    int main(){
        cin>>n;
        for(int i = 1;i <=n;i++){
            for(int j = 1;j <= i;j++){
                cin>>value[i][j];
            }
        }
        memset(dp,-1,sizeof(dp));
        dfs(1,1);
        cout<<dp[1][1];
        return 0;
    }
    //*必须经过一个点:”必须“转化为最优
        map[n/2][n/2] += hehe;
        for(int i = 1;i <= n;i++){
            for(int j = 1;j <= i;j++){
                dp[i][j] = max(dp[i-1][j],dp[i-1][j-1]) + map[i][j];
                ans = max(dp[i][j],ans);
            }
        }
        cout<<ans-hehe;
    //*对一个数取余:加状态
    dp[1][1][map[1][1]%100] = 1;
        for(int i = 2;i <= n;i++){
            for(int j = 1;j <= i;j++){
                for(int k = 0;k <= 99;k++){
                    if(dp[i-1][j-1][k] || dp[i-1][j][k]){
                        dp[i][j][(k+map[i][j])%100] = 1;
                        if(i == n) ans = max(ans,(k+map[i][j])%100);    
                    }
                }
            }
        }
    cout<<ans;
    //*第k大路线
        fo(i,1,n){
            fo(j,1,i){
                val[i][j] = read();
            }
        }
        fo(i,1,n){
            dp[n][i][1] = val[n][i];
            cnt[n][i] = 1;
        }
        int p1,p2;
        fd(i,1,n-1){
            fo(j,1,i){
                p1 = p2 = 1;
                while(cnt[i][j] < k  && (p1 <= cnt[i+1][j] || p2 <= cnt[i+1][j+1])){
                    if(p1 <= cnt[i+1][j] && (dp[i+1][j][p1] >= dp[i+1][j+1][p2] || p2 > cnt[i+1][j+1])) dp[i][j][++cnt[i][j]] = val[i][j] + dp[i+1][j][p1++];
                    else if(p2 <= cnt[i+1][j+1] && (dp[i+1][j][p1] < dp[i+1][j+1][p2] || p1 > cnt[i+1][j]))dp[i][j][++cnt[i][j]] = val[i][j] + dp[i+1][j+1][p2++];
                }
                sort(dp[i][j]+1,dp[i][j]+cnt[i][j]+1,cmp);
            }
        }
        cout<<dp[1][1][k];
    
    
    //2.经典模型:背包
    
    //*01背包、完全背包、多重背包
    for(int i = 1;i <= n;i++){
            if(a[i] == -1){//顺序滚动数组 
                for(int j = w[i];j <= m;j++){
                    dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
                }
            }
            if(a[i] == 1){//逆序滚动数组 
                for(int j = m;j >= w[i];j--){
                    dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
                }
            }
            if(a[i] >  1){//二进制拆包 
                if(a[i] * w[i] >= m){
                    for(int j = w[i];j <= m;j++){
                        dp[j] = max(dp[j],dp[j-w[i]]+v[i]);
                    }
                    continue;
                }
                int k = 1;
                for(;k<a[i];){
                    for(int j = m;j >= w[i]*k;j--){
                        dp[j] = max(dp[j],dp[j-w[i]*k]+v[i]*k);
                    }
                    a[i] -= k;
                    k = k<<1;
                }
                for(int j = m;j >= w[i]*a[i];j--){
                    dp[j] = max(dp[j],dp[j-w[i]*a[i]]+v[i]*a[i]);
                }
            }
        }
        cout<<dp[m]<<endl; 
        
    //*分组背包
     for(int i = 1;i <= cnt;i++){
        now = tran[i];
        for(int j = c;j >= 0;j--){
            for(int t = 0;t < itm[now].size();t++){
                if(j >= itm[now][t].w)dp[j] = max(dp[j],dp[j-itm[now][t].w] + itm[now][t].v);
            }
        }
    } 
    
    //*依赖背包
    for(int i = 1;i <= n;i++){
            scanf("%d%d%d",&v,&p,&q);
            if(q == 0){
                pt[++cnt] = i;
                vis[i] = 1;
                req[cnt] = v;
                mon[cnt] = p*v;
            }else{        
                tmp.w = v;
                tmp.v = p*v;
                bag[q].push_back(tmp);
            }
    
        }
        for(int i = 1;i <= cnt;i++){
            ccnt ^= 1;
            int g = pt[i];
            for(int j = req[i];j <= m;j++){
                dp[ccnt][j] = max(dp[ccnt^1][j],dp[ccnt^1][j-req[i]] + mon[i]);
            }
            if(bag[g].size() >= 1){
                for(int j = req[i] + bag[g][0].w;j <= m;j++){
                    dp[ccnt][j] = max(dp[ccnt][j],max(dp[ccnt^1][j],dp[ccnt^1][j-req[i]-bag[g][0].w] + mon[i] + bag[g][0].v));
                }
            }
            if(bag[g].size() >= 2){
                for(int j = req[i] + bag[g][1].w;j <= m;j++){
                    dp[ccnt][j] = max(dp[ccnt][j],max(dp[ccnt^1][j],dp[ccnt^1][j-req[i]-bag[g][1].w] + mon[i] + bag[g][1].v));
                }
                for(int j = req[i] + bag[g][1].w + bag[g][0].w;j <= m;j++){
                    dp[ccnt][j] = max(dp[ccnt][j],max(dp[ccnt^1][j],dp[ccnt^1][j-req[i]-bag[g][1].w-bag[g][0].w] + mon[i] + bag[g][1].v + bag[g][0].v));
                }
            }
            for(int j = 0;j <= m;j++){
                dp[ccnt][j] = max(dp[ccnt^1][j],dp[ccnt][j]);
            }
        }
        cout<<dp[ccnt][m];
        
    //*二维费用背包
        f[0][0]=1;
            for (int i=1;i<=n;i++)
            {
                if (a[i]>k)continue;
                for (int j=m;j>=1;j--)
                    for(int l=k;l>=a[i];l--)
                        f[j][l]|=f[j-1][l-a[i]];
            }
            for (int j=k;j>=0;j--)
            for (int i=m;i>=0;i--)
            if (f[i][j]){printf("%d
    ",j);return 0;}
            
    //*重量特别大:用价值当费用求最小值
        fo(i,1,n){
            w[i] = read();v[i] = read();
            mn += v[i];
        }
        memset(dp,127/3,sizeof(dp));
        dp[0] = 0;
        fo(i,1,n){
            fd(j,v[i],mn){
                dp[j] = min(dp[j],dp[j-v[i]] + w[i]);
                if(dp[j] <= m) ans = max(j,ans);
            }
        }
        cout<<ans; 
    
    //3.经典模型:区间
    
    //* 能量项链:断环为链
    for(int i = 1;i <= n;i++){
            cin>>value[i];
            value[i+n] = value[i];
            
        }
        for(int j = 2;j <= n + n;j++){
            for(int i = j - 1;i >= 1 && j - i  <= n;i--){
                for(int k = i;k < j;k++){
                    dp[i][j] = max(dp[i][j],dp[i][k] + dp[k+1][j] + value[i] * value[j+1] * value[k+1]);
                }
            }
        }
        int ans = 0;
        for(int i = 1;i <= n;i++) ans = max(ans,dp[i][i+n-1]);
        cout<<ans;
        
    //*石子归并(最大最小值) 
    cin>>n;
        for(int i = 1;i <= n;i++){
            cin>>value[i];
            value[i+n] = value[i];
            sum[i] = sum[i-1] + value[i];
            
        }
        for(int i = 1;i <= n;i++) sum[i+n] = sum[i] + sum[n];
        for(int j = 2;j <= n + n;j++){
            for(int i = j - 1;i >= 1 && j - i + 1 <= n;i--){
                dp[i][j] = maxnum;
                for(int k = i;k < j;k++){
                    dp[i][j] = min(dp[i][j],dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1]); 
                }
                for(int k = i;k < j;k++){
                    dps[i][j] = max(dps[i][j],dps[i][k] + dps[k+1][j] + sum[j] - sum[i-1]); 
                }
            }
        }
        int ans = maxnum;
        for(int i = 1;i <= n;i++) ans = min(ans,dp[i][i+n-1]);
        cout<<ans<<endl;
        ans = 0;
        for(int i = 1;i <= n;i++) ans = max(ans,dps[i][i+n-1]);
        cout<<ans; 
        
    //*石子归并加强1:四边形不等式加速
    for(int l = 2;l <= n;l++){
            for(int i = 1;i <= n - l + 1;i++){
                j = i + l - 1;
                dp[i][j] = maxnum;
                for(int k = a[i][j-1];k <= a[i+1][j];k++){
                    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];
                        a[i][j] = k;
                    }
                    
                }
                   
            }
        }
        cout<<dp[1][n];
        
    //*石子归并加强2:三颗石子合并,开一个辅助数组记录两堆合并结果
        n=read();
        rep(i,1,n)
            rep(j,1,n)f1[i][j]=f2[i][j]=INF;
        rep(i,1,n)a[i]=a[i-1]+read();
        rep(i,1,n)f2[i][i]=0;
        rep(i,1,n-1)f1[i][i+1]=a[i+1]-a[i-1];
        rep(len,3,n)
        rep(i,1,n-len+1){
            int j=i+len-1;
            rep(k,i,j-1)f2[i][j]=min(f2[i][j],f1[i][k]+f2[k+1][j]+a[j]-a[k]);
            rep(k,i,j-1)f1[i][j]=min(f1[i][j],f2[i][k]+f2[k+1][j]+a[j]-a[i-1]);
        }
        cout<<f2[1][n]<<endl;
     
    //*加分二叉树:输出方案
    void preorder(int i,int j){
        int k = root[i][j];
        if(k == 0) return;
        cout<<k<<" ";
        preorder(i,k-1);
        preorder(k+1,j);
    }
    int main(){
        memset(root,0,sizeof(root));
        memset(f,0,sizeof(f));
        memset(d,0,sizeof(d));
        
        cin>>n;
        for(int i=1;i <= n;i++) cin>>d[i];
        for(int i = 0;i <= n;i++){
            f[i][i] = d[i];
            root[i][i] = i;
            f[i+1][i] = 1;
        }
        for(int p = 1;p < n;p++){
            for(int i = 1;i <= n - p;i++){
                int j = i+p;
                for(int k = i;k <= j;k++){
                    int temp = f[i][k-1] * f[k+1][j] + d[k];
                    if(temp > f[i][j]) f[i][j] = temp,root[i][j] = k;
                }
            }
        }
        cout<<f[1][n]<<endl;
        preorder(1,n);
        return 0;
    } 
     
    //*最优矩阵链乘
        for(int i = 1;i <= n+1;i++){
            cin>>a[i];
        }
        for(int i = 1;i <= n+1;i++){
            for(int j = 1;j <= n+1;j++){
                f[i][j] = maxint;
            }
        }
        long long j,tmp;
        for(int l = 3;l <= n+1;l++){
            for(int i = 1;i <= n-1;i++){
                j = i + l - 1;
                for(int k = i + 1;k <= j-1;k++){
                    tmp = a[i] * a[k] * a[j];
                    if(k - i >= 2) tmp += f[i][k];
                    if(j - k >= 2) tmp += f[k][j];
                    f[i][j] = min(f[i][j],tmp);
                }
            }
        }
        cout<<f[1][n+1]; 
     
    //*括号序列
    int main(){
        scanf("%s",a+1);
        a[0] = '!';
        n = strlen(a) - 1;
        for(int i = 0;i <= n;i++){
            for(int j = 0;j <= n;j++){
                f[i][j] = 999;
                if(j == i - 1) f[i][j] = 0;
                if(i == j) f[i][j] = 1;
            }
        }
        for(int l = 2;l <= n;l++){
            for(int i = 1;i <= n - l + 1;i++){
                int j = i + l - 1;
                if((a[i] == '(' && a[j] == ')') || (a[i] == '[' && a[j] == ']')) f[i][j] = min(f[i][j],f[i+1][j-1]);
                if(a[i] == '(' || a[i] == '[') f[i][j] = min(f[i][j],f[i+1][j] + 1);
                if(a[j] == ')' || a[j] == ']') f[i][j] = min(f[i][j],f[i][j-1] + 1);
                for(int k = i;k < j;k++) f[i][j] = min(f[i][j],f[i][k] + f[k+1][j]);
            }
        }
        cout<<f[1][n];
        return 0;
    } 
    
    //4.经典模型:序列
    
    //*LIS
        for(int i = 1;i <= n;i++){
            dp[i] = 1;
            for(int j = 1;j < i;j++){
                if(orz[i] > orz[j])dp[i] = max(dp[i],dp[j]+1);
                ans = max(ans,dp[i]);
            }
        }
        cout<<ans;
        
    //*二分优化LIS:辅助数组
    int main(){
        cin>>n;
        for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
        for(int i = 1;i <= n;i++) g[i] = inf;
        for(int i = 1;i <= n;i++){
            int k = lower_bound(g+1,g+1+n,a[i]) - g;
            d[i] = k;
            g[k] = a[i];
            if(ans < d[i]) ans = d[i];
        }
        cout<<ans;
        return 0;
    }
    
    //*LCS
        fo(i,1,n){
            fo(j,1,n){
                if(a[i] == b[j]) dp[i][j] = dp[i-1][j-1] + 1;
                else dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
            }
        }
    
    //*LCS两序列对应相同:转化为LIS
        n = read();
        fo(i,1,n) a[i] = read();
        fo(i,1,n){
            b[i] = read();
            tran[b[i]] = i;
        }
        fo(i,1,n){
            b[tran[a[i]]] = i;
        }
        memset(g,127/3,sizeof(g));
        int ans = 0;
        fo(i,1,n){
            int k = lower_bound(g+1,g+1+n,b[i]) - g;
            d[i] = k;
            g[k] = b[i];
            ans = max(ans,d[i]);
        }
        cout<<ans;
    
    //5.树形dp
    
    //*选课:左儿子右兄弟
    void dfs(int item,int room){
        if(dp[item][room] >=0) return;
        if(item == 0 || room == 0){
            dp[item][room] = 0;
            return;
        }
        dfs(brother[item],room);
        for(int k = 0;k < room;k++){
            dfs(brother[item],k);
            dfs(child[item],room-k-1);
            dp[item][room] = max(dp[item][room],max(dp[brother[item]][room],dp[brother[item]][k] + dp[child[item]][room-k-1] + value[item]));
        }
        return;
    }
    
    int main(){
        memset(dp,-1,sizeof(dp));
        cin>>n>>m;
        for(int i = 1;i <= n;i++){
            cin>>tmpa>>tmpb;
            value[i] = tmpb;
            if(tmpa == 0) tmpa = n + 1;
            brother[i] = child[tmpa];
            child[tmpa] = i;
        }
        dfs(child[n+1],m);
        cout<<dp[child[n+1]][m];
        return 0;
    }
    
    //*没有上司的舞会
    int n,happy[maxn],vis[maxn],dp[maxn][2],root;
    vector<int> l[maxn];
    void input(){
        cin>>n;
        for(int i = 1;i <= n;i++){
            scanf("%d",&happy[i]);
        }
        int k,t;
        for(int i = 1;i < n;i++){
            scanf("%d%d",&t,&k);//k boss
            vis[t] = 1;
            l[k].push_back(t);
            
        }
        
        for(int i = 1;i <= n;i++){
            if(!vis[i]) root = i;
            dp[i][0] = dp[i][1] = -inf;
        }
    }
    void dfs(int now){    
        dp[now][1] = happy[now];
        dp[now][0] = 0;
        for(int i = 0;i < l[now].size();i++){
            dfs(l[now][i]);    
            dp[now][1] += dp[l[now][i]][0];
            dp[now][0] += max(dp[l[now][i]][1],dp[l[now][i]][0]);
        }
    }
    int main(){
        input();
        dfs(root);
        cout<<max(dp[root][0],dp[root][1]);
        return 0;
    }
    
    //*医疗两仪师:把一棵树分成k+1个联通块,使得每一个连通块有且仅有一个标记点,求方案数,方程都是一样的,给两个版本
    
    void input(){
        cin>>n;
        int cmd;
        for(int i = 1;i < n;i++){
            cin>>cmd;
            add_edge(cmd,i);
        }
        for(int i = 0;i < n;i++){
            cin>>col[i];
        }
    }
    void dfs(int u){
        if(!head[u]){
            if(col[u]) f[u][1] = 1;
            else f[u][0] = 1;
            return;
        }
        ll sum = 1;
        int v;
        if(col[u]){
            for(int i = head[u];i;i = e[i].nxt){
                v = e[i].to;
                dfs(v);
                sum = q_mul(sum,f[v][0] + f[v][1]);
            }
            f[u][1] = sum;
            return;
        }else{
            for(int i = head[u];i;i = e[i].nxt){
                v = e[i].to;
                dfs(v);
                sum = q_mul(sum,f[v][0] + f[v][1]);
            }
            f[u][0] = sum;
            for(int i = head[u];i;i = e[i].nxt){
                v = e[i].to;
                f[u][1] += q_mul(f[v][1],q_mul(sum,q_pow(f[v][0] + f[v][1],mod-2)));
            }
            return;
        }
    }
    int main(){
        freopen("tree.in","r",stdin);
        freopen("tree.out","w",stdout);
        ios::sync_with_stdio(false);
        input();
        dfs(0);
        cout<<f[0][1];
        return 0;
    }
    
    
    
    ll f[MAXN][2];
    int point[MAXN] = {0}, nxt[MAXN * 2] = {0}, v[MAXN * 2] = {0}, tot = 0;
    bool color[MAXN] = {0};
    int n;
    
    inline void addedge(int x, int y)
    {
        tot++;
        nxt[tot] = point[x]; point[x] = tot; v[tot] = y;
    }
    
    void dfs(int now, int father)
    {
        f[now][0] = 1;
        f[now][1] = 0;
        for (int tmp = point[now]; tmp; tmp = nxt[tmp])
            if (v[tmp] != father)
            {
                dfs(v[tmp], now);
                f[now][1] = (f[now][1] * f[v[tmp]][0]) % MOD;
                f[now][1] = (f[now][1] + f[now][0] * f[v[tmp]][1]) % MOD;
                f[now][0] = (f[now][0] * f[v[tmp]][0]) % MOD;
            }
        if (color[now])
            f[now][1] = f[now][0];
        else
            f[now][0] = (f[now][0] + f[now][1]) % MOD;
    }
    
    int main()
    {
        freopen("tree.in", "r", stdin);
        freopen("tree.out", "w", stdout);
        scanf("%d", &n);
        for (int i = 2; i <= n; i++)
        {
            int x;
            scanf("%d", &x);
            addedge(i, x + 1); addedge(x + 1, i);
        }
        for (int i = 1; i <= n; i++)
        {
            int x;
            scanf("%d", &x);
            if (x == 1) color[i] = true;
            else color[i] = false;
        }
        dfs(1, 0);
        cout << f[1][1] << endl;
    } 
     
    
    /***习题整理***/
    
    /*
    例1.子串
    1.f[i][j][k]表示ai,bj为结尾匹配k个,f[i][j][k] = f[i-1][j-1][k] + f[1...i-1][j-1][k-1]
    2.f[i][j][k]表示匹配到i,j匹配k个,f[i][j][k] = f[i-1][j-1][k] + f[i-x...i][j-x...j][k-1] if matches
    显然前者可以优化后者不行 
    */ 
    1.   dp[0][0][0] = 1;
        fo(i,0,n) sum[0][i][0] = 1;
        fo(k,1,K){
            cnt^=1;
            fo(i,1,n){
                fo(j,1,m){
                    if(a[i] == b[j]){
                        dp[cnt][i][j] = (dp[cnt][i-1][j-1] + sum[cnt^1][i-1][j-1]) % mod;
                    }
                    sum[cnt][i][j] = (sum[cnt][i-1][j] + dp[cnt][i][j]) % mod;
                }
            }
            memset(sum[cnt^1],0,sizeof(sum[cnt^1]));
            memset(dp[cnt^1],0,sizeof(dp[cnt^1]));
        }
        cout<<sum[cnt][n][m];
        
    2.  fo(i,0,n) dp[0][i][0] = 1;
        fo(k,1,K){
            cnt^=1;
            fo(i,1,n){
                fo(j,1,m){
                    fo(x,0,9999){
                        if(a[i-x] != b[j-x] || i <x || j < x) break;
                        dp[cnt][i][j] = (dp[cnt][i][j] + dp[cnt^1][i-x-1][j-x-1]) % mod;
                    }
                    dp[cnt][i][j] = (dp[cnt][i][j] + dp[cnt][i-1][j]) % mod;
                }
            }
            memset(sum[cnt^1],0,sizeof(sum[cnt^1]));
            memset(dp[cnt^1],0,sizeof(dp[cnt^1]));
        }
        cout<<dp[cnt][n][m];
        
    /*
    例2:k车问题,在n*m棋盘上放置K个车,使没有一个车同时被两个车攻击,求方案数
    i:行数,j:只有一个车且安全的列数,l:只有一个车且不安全的列数,p:有两个车的列数
    */
    f[0][0][0][0] = 1;
        for(int i = 1;i <= n;i++){
            cnt ^= 1;
            for(int j = 0;j <= n<<1;j++){
                for(int l = 0;l <= n<<1;l+=2){
                    for(int p = 0;p <= n;p++){
                        f[cnt][j][l][p] = f[cnt^1][j][l][p];
                        if(j > 0) f[cnt][j][l][p] += f[cnt^1][j-1][l][p]*(m-j-p-l+1);
                        if(p > 0) f[cnt][j][l][p] += f[cnt^1][j+1][l][p-1]*(j+1);
                        if(l > 1) f[cnt][j][l][p] += (f[cnt^1][j][l-2][p]*(m-j-l-p+2)*(m-j-l-p+1))>>1;
                        f[cnt][j][l][p] %= mod;
                        if(i == n && j + l + (p<<1) == k){
                            ans = (ans + f[cnt][j][l][p]) % mod;
                        }
                    }
                }
            }
        }
        cout<<ans;
        
    /*
    例3:滑雪,记忆化搜索 
    */
    int dp(int i,int j){
        if(f[i][j]) return f[i][j];
        f[i][j] = 1;
        int y,x;
        for(int t = 0;t < 4;t++){
            y = i + dy[t];
            x = j + dx[t];
            if(jud(y,x) && h[i][j] > h[y][x]) f[i][j] = max(f[i][j],1 + dp(y,x));
        }
        return f[i][j];
    }
    int main(){
        cin>>r>>c;
        for(int i = 1;i <= r;i++){
            for(int j = 1;j <= c;j++){
                cin>>h[i][j];
            }
        }
        for(int i = 1;i <= r;i++){
            for(int j = 1;j <= c;j++){//attention!
                ans = max(ans,dp(i,j));
            }
        }
        cout<<ans;
        return 0;
    }
     
    /*
    例4:传纸条:“必须”变“最优“ + 优化空间 
    */
    for(int i = 1;i <= m;i++){
            for(int j = 1;j <= n;j++){
                scanf("%d",&v[i][j]);
            }
        }
                for(int l = 2;l <= n+m;l++)
                for(int y = 1;y <= m && y < l;y++){
                    for(int i = 1;i <= m && i < l;i++){
                    int x = l - y, j = l - i;
                    f[l][i][y] = max(max(f[l-1][i][y],f[l-1][i-1][y]),max(f[l-1][i][y-1],f[l-1][i-1][y-1]));
                    f[l][i][y] += v[i][j];
                    if(!(i == y && j == x)) f[l][i][y] += v[y][x];
                    }
                }
        cout<<f[m+n][m][m];
    
    /*
    例5:划分大理石,价值为i([1,6])的物品各ai件,问能否划分成价值相等的两部分 
    总价值为奇数不能划分,多重背包判断能否实现价值为sumv/2 
    */
    int main(){
        bool ok = true,ans = false;
        while(ok){
            ok = false;
            n = sum = 0;
            for(int i = 1;i <= 6;i++){
                scanf("%d",&a[i]);
                if(a[i]) ok = true;
                sum += a[i]*i;
                for(int j = 1;j <= a[i];j<<=1){
                    w[++n] = j * i;
                    a[i] -= j;
                }
                w[++n] = a[i] * i;
            }
            if(!ok) break;
            if(sum & 1){
                puts("Can't");
                continue;
            }
            memset(f,false,sizeof(f));
            f[0] = true;
            for(int i = 1;i <= n;i++){
                for(int j = sum >> 1;j >= w[i];j--){
                    if(f[j - w[i]]) f[j] = true;
                }
                if(f[sum>>1]){
                    ans = true;
                    break;
                }
            }
            if(ans) puts("Can");
            else puts("Can't");
        }
        return 0;
    }
    
    /*
    例6:集体舞,给一个正三角形,每一个点为一个小三角形,且分为两类,求种类相同的最大三角形面积
    把大三角拆成小三角 
    */
    int main(){
        cin>>n;
        char cmd;
        sum[1] = 1;
        for(int i = 1;i <= n;i++){
            for(int l = 1;l <= 2*(n-i) + 1;l++){
                int j = i + l - 1;
                scanf("%c",&cmd);
                while(cmd != '-' && cmd != '#') scanf("%c",&cmd);
                if(cmd == '#') a[i][j] = 1;
                else a[i][j] = 2;
            }
        }
        for(int i = 3;i <= 2 * n - 1;i += 2) sum[i] = sum[i-2] + i;
        for(int i = 1;i <= n;i++){
            for(int l = 1;l <= 2*(n-i) + 1;l++){
                int j = i + l - 1;
                if(a[i][j] == 2) f[i][j] = 1;
                if(l & 1) 
                    if(a[i][j] == 2 && a[i][j+1] == 2 && a[i][j+2] == 2 && a[i+1][j+1] == 2) f[i][j] = 3;
                ans = max(ans,f[i][j]);
            }
        }
        for(int k = 5;k <= 2 * n - 1;k += 2){
            for(int i = 1;i <= n;i++){
                for(int l = 1;l <= 2*(n-i) + 1;l++){
                    int j = i + l - 1;
                    if((l & 1) && f[i][j] >= k - 2 && f[i][j+2] >= k - 2 && f[i+1][j+1] >= k - 2){
                        f[i][j] = k;
                        ans = max(ans,k);
                    }
                }
            }
        }
        cout<<sum[ans];
        return 0;
    }
    /*
    例7:chopsticks,定义三元组(x,y,z)的代价为最小的两个数的差的平方,在n个数中选出m个三元组,求最小代价
    经典题目,将原数列倒序排序,保证数的个数就可以保证最大的数z一定取到,剩下的两个数贪心选择相邻的 
    */
        for(int i = 1;i <= m;i++){
            for(int j = 1;j <= n;j++){
                if(j < i * 3) dp[i][j] = 9876543212345L;
                else dp[i][j] = min(dp[i][j-1],dp[i-1][j-2] + (a[j] - a[j-1]) * (a[j] - a[j-1]));
            }
        }
        cout<<dp[m][n];
    /*
    例8:自然数拆分,把一个数拆成若干数相加,加数可重复,分两种情况讨论:增加一个1,把以前的情况都加上1 
    */
        dp[0][0] = 1;
        for(int i = 1;i <= n;i++){
            for(int j = i;j <= n;j++){
                dp[i][j] = (dp[i-1][j-1] + dp[i][j-i]) % mod;
                if(i > 1 && j == n) ans = (ans + dp[i][j]) % mod;
            }
        }
        cout<<ans;
    
    /*
    例9:smrtfun, 有n个二元组(ai,bi),定义其价值为ai+bi,限制suma、sumb都为非负,求最大价值和
    加一维记录suma,加上一个数保证其非负 
    */
    int n,a[105],b[105],f[105][200015],suma = 100000,ans,bg = 100000,inf = 100005;
    int main(){
        cin>>n;
        for(int i = 1;i <= n;i++){
            scanf("%d%d",&a[i],&b[i]);
            if(a[i] >= 0) suma += a[i];
        }
        for(int i = 0;i <= bg * 2;i++) f[1][i] = -inf;
        f[1][bg+a[1]] = b[1];
        f[1][bg] = 0;
        for(int i = 2;i <= n;i++){
            for(int j = 0;j <= bg * 2;j++){
                f[i][j] = f[i-1][j];
                if(j - a[i] >= 0 && j - a[i] <= bg * 2) f[i][j] = max(f[i][j],f[i-1][j-a[i]] + b[i]);
                if(j >= bg && f[i][j] >= 0) ans = max(ans,j + f[i][j] - bg);
            }
        }
        cout<<ans; 
        
        return 0;
    }
    
    /*
    例10:将一个自然数拆成若干个2的整数次方之和,求方案数
    nlogn按照最大的数字分类讨论,n按有没有1讨论(这种讨论是常见套路) 
    */
    int main(){
        cin>>n;
        f[0][0] = 1;
        for(int i = 1;i <= n;i++){
            for(int j = 1,l = 1;l <= i;j++,l <<= 1){
                h[i] = j;
                f[j][i] = f[j-1][i];
                k = i - l;
                if(l >= k) f[j][i] = (f[j][i] + f[h[k]][k]) % mod;
                else f[j][i] = (f[j][i] + f[j][k]) % mod;
            }
        }
        cout<<f[h[n]][n];
        return 0;
    }
    int main(){
        cin>>n;
        f[1] = 1;
        f[2] = 2;
        for(int i = 3;i <= n;i++){
            if(!(i & 1)) f[i] = (f[i-1] + f[i>>1]) % mod;
            else f[i] = f[i-1];
        }
        cout<<f[n];
        return 0;
    }
    
    /*
    例11:股票,1块钱买入卖出股票,交易数量可以是分数,求问最多赚多少钱
    贪心购买 
    */
    double a[1000005],ans;
    int main(){
        cin>>n;
        for(int i = 1;i <= n;i++) scanf("%lf",&a[i]);
        ans = 1;
        for(int i = 1;i <= n;i++){
            if(a[i] < a[i+1] && !ch) ch = i;
            else if(a[i] > a[i+1] && ch){
                ans = ans / a[ch] * a[i];
                ch = 0;
            }
        }
        pt = floor(ans + 0.5);
        cout<<pt;
        return 0;
    }
    
    /*
    例12.任务安排:N个任务排成一个序列在一台机器上等待完成(顺序不得改变),这N个任务被分成若干批,每批包含相邻的若干任务。从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti。在每批任务开始前,机器需要启动时间S,而完成这批任务所需的时间是各个任务需要时间的总和(同一批任务将在同一时刻完成)。每个任务的费用是它的完成时刻乘以一个费用系数Fi。请确定一个分组方案,使得总费用最小。
    费用提前计算 
    */
        for(int i = 1;i <= n;i++){
            for(int j = i;j >= 1;j--){
                dp[i] = min(dp[i],dp[j-1] + (s + sumt[i] - sumt[j-1]) * (sumf[n] - sumf[j-1]));
            }
        }
        
    /*
    例13.盖房子:正方形嵌套 
    */
        for(int i = 1;i <= n;i++){
            for(int j = m;j >= 1;j--){
                if(f[i][j]) f[i][j] = min(min(f[i-1][j],f[i-1][j+1]),f[i][j+1]) + 1;
                ans = max(ans,f[i][j]);
            }
        }
    
    /*
    例14.mm不哭:提前计费 
    */
    int main(){
        cin>>n>>v;
        for(int i = 1;i <= n;i++){
            scanf("%d%d",&mm[i].d,&mm[i].w);
        }
        sort(mm+1,mm+1+n,cmp);
        for(int i = 0;i <= n;i++){
            for(int j = 0;j <= n;j++){
                lf[i][j] = rf[i][j] = 1087654321;
            }
        }
        for(int i = 1;i <= n;i++) sumw[i] = sumw[i-1] + mm[i].w;
        lf[v][v] = rf[v][v] = 0;
        for(int l = 1;l <= n - 1;l++){
            for(int i = max(v - l,1);i <= v;i++){
                int j = i + l;
                if(j > n) continue;
                lf[i][j] = min(lf[i+1][j] + (mm[i+1].d - mm[i].d) * (sumw[i] + sumw[n] - sumw[j]),rf[i+1][j] + (mm[j].d - mm[i].d) * (sumw[i] + sumw[n] - sumw[j]));
                rf[i][j] = min(rf[i][j-1] + (mm[j].d - mm[j-1].d) * (sumw[i-1] + sumw[n] - sumw[j-1]),lf[i][j-1] + (mm[j].d - mm[i].d) *(sumw[i-1] + sumw[n] - sumw[j-1]));
            }
        }
        cout<<(lf[1][n] < rf[1][n] ? lf[1][n] : rf[1][n]);
        return 0;
    }
    
    /*
    例15.搭建双塔,n个石头高度为hi,求搭建两个一样的塔的最大高度
    两个塔就本质上来说都是一致的,加一维表示差值,转移显然,注意初值设-1表示达不到 
    */
     for(int i = 0;i <= n;i++){
            for(int j = 0;j <= 2000;j++){
                f[i][j] = -1;
            }
        }
        f[0][0] = 0;
        for(int i = 1;i <= n;i++){
            for(int j = 0;j <= 2000;j++){
                f[i][j] = max(f[i][j],f[i-1][j]);
                if(f[i-1][j] != -1){
                    f[i][j+a[i]] = max(f[i][j+a[i]],f[i-1][j]);
                    if(a[i] <= j) f[i][j-a[i]] = max(f[i][j-a[i]],f[i-1][j] + a[i]);
                    else f[i][a[i]-j] = max(f[i][a[i]-j],f[i-1][j] + j);
                }
            }
        }
        if(f[n][0]) cout<<f[n][0];
        else cout<<"Impossible";
        
    /*
    例16.飞扬的小鸟,在某一位置点击升高yi,不点击降低xi,求到终点最少点几次,或最多能过几个管子
    类似于完全背包,细节非常多 
    */
    int main(){
        cin>>n>>m>>k;
        for(int i = 0;i < n;i++){
            cin>>up[i]>>down[i];
            
        }
        int p,l,h;    
        for(int i = 1;i <= n+1;i++) {
            for(int j = 0;j <= m;j++){
                dp[i][j] = maxnum;
            }
            upp[i-1].high = m+1;
            upp[i-1].low = 0;
        }
        dp[0][0] = maxnum;
        int arrive = k;
        for(int i = 1;i <= k;i++){
            cin>>p>>l>>h;
            upp[p].high = h;
            upp[p].low = l;
            vis[p] = 1;
        }
        for(int i =  1;i <= n;i++){
            for(int j = 1;j <= m;j++){
                if(j >= up[i-1]){//点1下,或者大于1下,相当于是一个混合背包 
                dp[i][j] = min(dp[i][j],min(dp[i-1][j-up[i-1]]+1,dp[i][j-up[i-1]]+1));
                }
                if(j == m){//碰到天花板的情况单独讨论 
                    for(int q = m-up[i-1] ;q <= m;q++) dp[i][j] = min(dp[i][j],min(dp[i-1][q] + 1,dp[i][q] + 1));
                }
                
            }//往下走的情况 
            for(int j = upp[i].low+1;j < upp[i].high;j++) if(j + down[i-1] <=m) dp[i][j] = min(dp[i][j], dp[i-1][j+down[i-1]]);
            for(int j = 1;j <= upp[i].low;j++) dp[i][j] = maxnum;
            for(int j = upp[i].high;j <= m;j++) dp[i][j] = maxnum;
        }
        int cnt = k, ans = maxnum;//寻找在哪个管子处停止 
        for (int i = n; i >= 1; i--) {
            for (int j = upp[i].low+1; j <= upp[i].high-1; ++j)
                if (dp[i][j] < maxnum)
                   ans = min(ans, dp[i][j]);
            if (ans != maxnum) break;
            if (upp[i].high <= m)
               cnt --;
        }
        if(cnt==k)
            printf("1
    %d
    ", ans);
        else 
            printf("0
    %d
    ", cnt);
        return 0;
    }
    /*
    例17.过河
    压缩路径 
    */
    int main(){
        cin>>l>>s>>t>>m;
        int tmp,next,last = 0,cut = 0;
        for(int i = 1;i <= m;i++){
            scanf("%d",&tmp);
            stone[i] = tmp;
        }
        sort(stone+1,stone+1+m,cmp);
        stone[m+1] = l;
        for(int i = 1;i <= m+1;i++){
            tmp = stone[i];
            tmp -= cut;
            int river = tmp - last - 1;
            int rec = river;
            river %= s*t*10;
            tmp = last+river+1;
            vis[tmp] = 1;
            last = tmp;
            if(i == m+1) l = tmp;
        }
        for(int i = 1;i <= l;i++){
            dp[i] = maxint;
        }
        ans = maxint;
        for(int i = 0;i < l;i++){
            if(vis[i]){
                dp[i]++;
            }
            for(int j = s;j <= t;j++){
                if(i+j>=l){
                    ans = min(ans,dp[i]);
                    break;
                }
                dp[i+j] = dp[i+j] > dp[i] ? dp[i] : dp[i+j];
                
            }
        }
        cout<<ans;
        return 0;
    }
  • 相关阅读:
    批量重命名工具 Bulk Rename Utility
    Makefile中的ifeq 多条件使用
    利用Python批量下载邮件附件
    在线随机抽取工具、在线汉字转拼音
    《如何把事情做到最好》读书笔记
    Android 通过adb快速恢复出厂设置
    Makefile的ifeq逻辑或,逻辑与的变通实现
    Android 获取后台正在运行的第三方应用列表
    Android.mk 中 filter 和 filterout 的用法
    Android TV端电视直播软件 和 投屏工具
  • 原文地址:https://www.cnblogs.com/hyfer/p/6043594.html
Copyright © 2020-2023  润新知