Ciel and Gondolas  CodeForces – 321E

题意:有n个人,第i个人和第j个人放在一起时会产生a[i][j]的沮丧值(是社恐吗),保证a[i][j]=a[j][i],两个人只产生一份沮丧值。将n个人分成连续的k组,每组产生的沮丧值是这组人两两之间产生的沮丧值之和。求所有沮丧值的最小值。

解:设dp[i][j]为将前j个人分为i组,显然有dp[i][j]=min(dp[i-1][k]+sum(k,j)). 现在sum(k,j)和k有关系了,不能用单调队列优化。引入决策单调性。对于每一个dp[i][j],都会选择一个k进行转移,称k为决策点。决策单调性指后作出决策所选择的决策点,一定不在之前决策选择的决策点前面。

推荐读物:关于决策单调性优化动态规划

这道题属于上文中决策点之间不影响的情况,可以用分治实现。但一开始写了二分,然后T飞了……存个代码。

代码:


#include <bits/stdc++.h>
using namespace std;
#define maxx 4005
#define maxn 25
#define maxm 205
#define ll long long
#define inf 1000000009
#define mod 2520
int dp[805][maxx]={0};
int sum[maxx][maxx]={0};
int q[maxx]={0};
int n,k;
int read(int res = 0) {char ch = getchar(); while (!isdigit(ch)) ch = getchar(); while (isdigit(ch)) res = res * 10 + (ch - '0'), ch = getchar(); return res;}
int cal(int l,int r){
    return (sum[r][r]-sum[l-1][r]-sum[r][l-1]+sum[l-1][l-1])/2;
}
int get(int s,int e,int i){
    int l=e+1,r=n;
    int res=0x3f3f3f3f;
    while(l<=r){
        int mid=(l+r)/2;
        if(dp[i-1][s]+cal(s+1,mid)>=dp[i-1][e]+cal(e+1,mid)){
            res=mid;
            r=mid-1;
        }
        else{
            l=mid+1;
        }
    }
    return res;
}
signed main() {
    n=read();
    k=read();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            int w=read();
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+w;
        }
    }
    for(int i=1;i<=n;i++){
        dp[0][i]=0x3f3f3f3f;
    }
    for(int i=1;i<=k;i++){
        int head=1,tail=1;
        q[1]=0;
        for(int j=1;j<=n;j++){
            while(head<tail&&get(q[head],q[head+1],i)<=j) head++;
            dp[i][j]=dp[i-1][q[head]]+cal(q[head]+1,j);
            while(head<tail&&get(q[tail-1],q[tail],i)>=get(q[tail],j,i)) tail--;
            q[++tail]=j;
        }
    }
    printf("%d\n",dp[k][n]);
    return 0;
}
//dp[i][j]=min(dp[i-1][k]+val(k+1,j))

View Code

分治代码:


#include <bits/stdc++.h>
using namespace std;
#define maxx 4005
#define maxn 25
#define maxm 205
#define ll long long
#define inf 1000000009
#define mod 2520
int dp[805][maxx]={0};
int sum[maxx][maxx]={0};
int n,k;
inline int read(){
    int x=0; char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar());
    for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
    return x;
}
int cal(int l,int r){
    return (sum[r][r]-sum[l-1][r]-sum[r][l-1]+sum[l-1][l-1])/2;
}
int ii;
void solve(int l,int r,int s,int e){
    if(r<l||e<s) return;
    if(s==e){
        //printf("%d %d\n",l,r);
        for(int i=l;i<=r;i++){
            dp[ii][i]=dp[ii-1][s]+cal(s+1,i);
        }
        return;
    }
    int mid=(l+r)/2;
    int ans=s;
    for(int i=s;i<mid&&i<=e;i++){
        if(dp[ii][mid]>dp[ii-1][i]+cal(i+1,mid)){
            dp[ii][mid]=dp[ii-1][i]+cal(i+1,mid);
            ans=i;
        }
    }
    solve(l,mid-1,s,ans);
    solve(mid+1,r,ans,e);
}
signed main() {
    n=read();
    k=read();
    int x;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            x=read();
            sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+x;
        }
    }
    memset(dp,0x3f,sizeof dp);
    for(int i=0;i<=k;i++){
        dp[i][0]=0;
    }
    for(int i=1;i<=n;i++){
        dp[1][i]=cal(1,i);
    }
    for(int i=2;i<=k;i++){
        ii=i;
        solve(1,n,0,n-1);
    }
    printf("%d\n",dp[k][n]);
    return 0;
}
//dp[i][j]=min(dp[i-1][k]+val(k+1,j))

View Code

感觉这样的优化把第一次决策预处理好会舒服一点。

Yet Another Minimization Problem  CodeForces – 868F

题意:把上题的价值改为子数组内能组成多少两个元素相同的二元组,比如n个2可以组成n*(n-1)/2个这样的二元组,n不必须连续。

解:啊和上题套路一样一样的,处理val的时候用一种类似于莫队的方法,暴力加减(

代码:


#include <bits/stdc++.h>
using namespace std;
#define maxx 100005
#define maxn 25
#define maxm 205
#define ll long long
#define inf 1000000009
#define mod 2520
ll dp[25][maxx]={0};
int a[maxx]={0};
int n,k;
ll cnt[maxx]={0};
ll ans=0;
int L=1,R=1;
void upd(int c,int d){
    ans+=d*cnt[c]*(cnt[c]-1)/2;
}
ll cal(int l,int r){
    while(L<l) upd(a[L],-1),cnt[a[L]]--,upd(a[L],1),L++;
    while(L>l) L--,upd(a[L],-1),cnt[a[L]]++,upd(a[L],1);
    while(R>r) upd(a[R],-1),cnt[a[R]]--,upd(a[R],1),R--;
    while(R<r) R++,upd(a[R],-1),cnt[a[R]]++,upd(a[R],1);
    return ans;
}
int ii;
void solve(int l,int r,int s,int e){
    if(l>r||s>e) return;
    if(s==e){
        for(int i=l;i<=r;i++){
            dp[ii][i]=dp[ii-1][s]+cal(s+1,i);
        }
        return;
    }
    int mid=(l+r)/2,p=l;
    for(int i=s;i<mid&&i<=e;i++){
        if(dp[ii][mid]>dp[ii-1][i]+cal(i+1,mid)){
            dp[ii][mid]=dp[ii-1][i]+cal(i+1,mid);
            p=i;
        }
    }
    solve(l,mid-1,s,p);
    solve(mid+1,r,p,e);
}
signed main() {
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    cnt[a[1]]++;
    memset(dp,0x3f,sizeof dp);
    for(int i=1;i<=n;i++){
        dp[1][i]=cal(1,i);
    }
    for(int i=0;i<=k;i++){
        dp[0][i]=0;
    }
    for(int i=2;i<=k;i++){
        ii=i;
        solve(1,n,0,n-1);
    }
    printf("%lld\n",dp[k][n]);
    return 0;
}
//dp[i][j]=min(dp[i-1][k]+val(k+1,j))

View Code

 

原文地址:http://www.cnblogs.com/capterlliar/p/16920221.html

1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长! 2. 分享目的仅供大家学习和交流,请务用于商业用途! 3. 如果你也有好源码或者教程,可以到用户中心发布,分享有积分奖励和额外收入! 4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解! 5. 如有链接无法下载、失效或广告,请联系管理员处理! 6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需! 7. 如遇到加密压缩包,默认解压密码为"gltf",如遇到无法解压的请联系管理员! 8. 因为资源和程序源码均为可复制品,所以不支持任何理由的退款兑现,请斟酌后支付下载 声明:如果标题没有注明"已测试"或者"测试可用"等字样的资源源码均未经过站长测试.特别注意没有标注的源码不保证任何可用性