动态规划总结

//动态规划
//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\n",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\n%d\n", ans);
    else 
        printf("0\n%d\n", 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;
}

 

posted @ 2016-11-08 16:37  ACforever  阅读(629)  评论(0编辑  收藏  举报