心如止水
Je n'ai pas le temps
posts - 400,comments - 130,trackbacks - 0
我很少写较长的题解,但是对于这一题我觉得很有必要。
对于这道题,首先应该想到贪心:每次取差值最小的一对。但是这样的贪心策略很容易找到反例,而且N=5000的数据规模,十分有可能是O(n^2)的算法。
于是考虑动态规划。如果是DP,那么很容易想到如下的状态定义:d[i][j]表示用前j个数组成i个(x,y,z)数对的最小消耗。
另外一个很容易注意到的地方就是:对于一个最优决策中的任意一个数对(x,y,z),其中x和y必在有序的a[i]中相邻。关于这点用反证法不难证明,也很容易注意到。
对于(x,y,z)中的z应该如何决策的问题,之前一直令我迷惑,这一点应该是题目最难解决的问题。

考虑状态d[i][j]:
    对于x和y,有如下考虑:
        对于a[j],如果不使用a[j],那么d[i][j]=d[i][j-1];
        如果使用a[j],那么就和a[j-1]一起使用,d[i][j]=d[i-1][j-2]+w(a[i],a[i-1]);
    于是有了总的状态转移方程:d[i][j]=min{d[i][j-1],d[i-1][j-2]+w(a[i],a[i-1])};
    这应该不难理解,但是对于z的决策呢?
    如果把a[i]按降序排列,那么z的影响就可以忽略了!这样依然可以采用上面的方程。

考虑状态d[i][j]:
    如果j<3i,此时当前策略是不可行的,d[i][j]=INF;
    如果j>=3i,即j>=3(i-1)+3,j>3(i-1)+2,当前状态有效
        转移到d[i-1][j-2]时,至少剩余一个多余的a[k]
        由于序列降序,a[k]可以和a[j]、a[j-1]配对
        同时d[i-1][j-2]有效,可以继续递归。
        转移到d[i][j-1]
        若d[i][j-1]为无效状态,d[i][j-1]==INF,必然不会比上面那种转移方式优;
        若d[i][j-1]为有效状态,可以继续递归地进行下去。

以下是我的代码,采用的记忆化搜索:
#include<stdio.h>
#include
<string.h>
const long maxn=5007,INF=100000007;
long k,n,r[maxn],d[1007][maxn],w[maxn];
long min(long a,long b)
{
    
return (a<b?a:b);
}
void swap(long &a,long &b)
{
    
long t=a;a=b;b=t;
}
long dp(long kk,long nn)
{
    
if(d[kk][nn]!=-1)
        
return d[kk][nn];
    
if(nn<3*kk)
        d[kk][nn]
=INF;
    
else if(kk==0)
        d[kk][nn]
=0;
    
else
    {
        d[kk][nn]
=INF;
        d[kk][nn]
=min(dp(kk,nn-1),dp(kk-1,nn-2)+w[nn]);
    }
    
return d[kk][nn];
}
int main()
{
    
/*
    freopen("data.in","r",stdin);
    freopen("data.out","w",stdout);
    //
*/
    
long test;
    scanf(
"%ld",&test);
    
while(test--)
    {
        scanf(
"%ld%ld",&k,&n);k+=8;
        
for(long i=1;i<=n;i++)
            scanf(
"%ld",&r[i]);
        
//  Input
        for(long i=1,j=n;i<j;i++,j--)
            swap(r[i],r[j]);
        
for(long i=2;i<=n;i++)
            w[i]
=(r[i]-r[i-1])*(r[i]-r[i-1]);
        
for(long i=0;i<=k;i++)
            
for(long j=0;j<=n;j++)
                d[i][j]
=-1;
        
//  Init
        
//  DP
        printf("%ld\n",dp(k,n));
    }
return 0;
}


posted on 2010-02-19 21:01 lee1r 阅读(1374) 评论(2)  编辑 收藏 引用 所属分类: 题目分类:动态规划

FeedBack:
# re: UVa 10271 Chopsticks
2010-08-21 09:06 | Yip
感谢你的题解  回复  更多评论
  
# re: UVa 10271 Chopsticks
2012-09-02 10:48 | *^_^*
真心膜拜  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理