码迷,mamicode.com
首页 > 其他好文 > 详细

动态规划(1)

时间:2015-08-09 16:53:47      阅读:322      评论:0      收藏:0      [点我收藏+]

标签:

     今天放两道动态规划的题,本人心得是:认真寻找规律

第一题

多重部分和问题

      有n种不同大小数字ai,每种个mi个。判断是否可以从这些数字之中选出若干使它们的和恰好为k。(1≤n≤100,1≤ai,mi≤100000,1≤k≤100000)

样例输入

   3                           n

   3  5  8                   a1   a2  ...

   3  2  2                   m1  m2 ...

   17                         k

样例输出

      YES

方法一:

     dp[i,j](前i种数字是否能加成j)

     大家会立即推出此式子:

     dp[i,j]=(0≤k≤mi且k*ai≤j时存在使dp[i,j-k*ai]为true的k)

     当dp[n,k]=true时就是YES。

     那么,时间复杂度就有待考虑,每一个数都要试一次,最多会有100*100000个数,再加n层循环,则

     O(n∑imi)=O(100*100*100000)=O(109)

    时间完爆,满分是不可能的

方法二

    认为dp[i,j]指用前i种数加和得到j时第i种数最多能剩下几个,如果加不到则为-1,

①.若前i-1个数可加到j,则直接留下mi个数;

    ②.若无法加到或前i-1个数不可加到j-ai,则-1;

    ③.否则,...

    ∴             mi                (dp[i,j]≥0)

        dp[i,j]=-1                 (j<ai或者dp[i+1,j-ai]≤0)

                   dp[i+1,j-ai]   (其它) 

 程序如下:

var
  dp,a,m:array[0..100000]of longint;
  n,i,j,k:longint;
begin
  read(n);
  for i:=1 to n do
    read(a[i]);
  for i:=1 to n do
    read(m[i]);
  read(k);
  for i:=1 to k do dp[i]:=0;
  for i:=1 to n do
    for j:=0 to k do
      if dp[j]>=0 then dp[j]:=m[i]
      else if (j<a[i])or(dp[j-a[i]]<=0) then dp[j]:=-1
      else dp[j]:=dp[j-a[i]]-1;
  if dp[k]>=0 then write(‘YES‘)
  else write(‘NO‘);
end.

C++略,

第二题(poj7624)

山区建小学

总时间限制: 
1000ms
 
内存限制: 
65536kB
描述

政府在某山区修建了一条道路,恰好穿越总共m个村庄的每个村庄一次,没有回路或交叉,任意两个村庄只能通过这条路来往。已知任意两个相邻的村庄之间的距离为di(为正整数),其中,0 < i < m。为了提高山区的文化素质,政府又决定从m个村中选择n个村建小学(设 0 < n < = m < 500 )。请根据给定的m、n以及所有相邻村庄的距离,选择在哪些村庄建小学,才使得所有村到最近小学的距离总和最小,计算最小值。

输入
第1行为m和n,其间用空格间隔
第2行为(m-1) 个整数,依次表示从一端到另一端的相邻村庄的距离,整数之间以空格间隔。

例如
10 3
2 4 6 5 2 4 3 1 3
表示在10个村庄建3所学校。第1个村庄与第2个村庄距离为2,第2个村庄与第3个村庄距离为4,第3个村庄与第4个村庄距离为6,...,第9个村庄到第10个村庄的距离为3。
输出
各村庄到最近学校的距离之和的最小值。
样例输入
10 2
3 1 3 1 1 1 1 1 3
样例输出
18

 刚开始以为这题很弱,可是画了画,就------;

先从简单的开始

①.当我要在第i个村庄到第j个村庄建1个学校,最小值怎么求?

大家应该知道,把学校放在村庄之中,总共走的路才最少,小学初中数学奥赛都有讲到,

那么,最小值怎么求?见下图:

1.五个点:.____.____.____.____. ,则最小路程:

                ____ ____ ____ ____ 

                        ____ ____  

发现  ____ ____ ____                   ____ ____ ____                       ____ ____ 

                ____ (靠左的)     +              ____ (靠右的)  —      (第一层中间两个线段) 

____ 为两点间距离,每个距离是不一样的

所以可知p[i,j]=p[i,j-1]+p[i+1,j]-p[i+1,j-1](j-i为偶数时)

2.p[i,j]=p[i,j-1]+p[i+1,j]-p[i+1,j-1]+a[(j+i) div 2](j-i为奇数时)

   红色部分为中间的线段,如果不仔细,极其难发现!!!请自行画图探究;

1个学校处理完,再处理k个:

dp[i(前i个山村),k]=min(dp[t(k-1≤t≤i-1),k-1]+p[t+1,i]

相信大家刚处理的时候会出现错误,或O(5005)或O(5004)的时间效率,此时要学会减少循环次数,让

dp变成二维。此题用到两次dp,一次预处理,一次为主要部分,有难度,让我耗费1个小时多!

那么,学会降层,程序如下:

var
  a:array[1..500]of longint;
  dp,p:array[1..500,1..500]of longint;
  n,i,j,k,m:longint;
function min(a,b:longint):longint;
begin
  if a<b then exit(a) else exit(b);
end;
begin
  read(n,m);
  for i:=1 to n-1 do
    read(a[i]);
  for i:=0 to n do
    for j:=1 to n do
      if j+i<=n then
        begin
          if i=0 then p[j,j]:=0
          else if i=1 then p[j,j+i]:=a[j]
          else if i mod 2=0 then p[j,j+i]:=p[j,j+i-1]+p[j+1,j+i]-p[j+1,j+i-1]
          else p[j,j+i]:=p[j,j+i-1]+p[j+1,j+i]-p[j+1,j+i-1]+a[(j+j+i) div 2];
        end;
  //一种非常好的处理方法,在循环变量下做文章,使预处理得以成功进行
  for i:=1 to n do
    for k:=1 to min(i,m) do
      if k=i then dp[i,k]:=0
      else if k=1 then dp[i,k]:=p[1,i]
      else
        begin
          dp[i,k]:=maxlongint;
          for j:=i-1 downto k-1 do
            dp[i,k]:=min(dp[i,k],dp[j,k-1]+p[j+1,i]);
        end;
  writeln(dp[n,m]);
end.
#include<iostream>
using namespace std;
int min(int a,int b)
{
    if (a<b) return a; else return b;
 } 
int a[501],dp[501][501],p[501][501],n,i,j,k,m;
int main()
{
    cin>>n>>m;
    for (i=1;i<=n-1;i++) cin>>a[i];
    for (i=0;i<=n;i++)
      for (j=1;j<=n;j++)
        {
            if (j+1<=n)
              {
                  if (i==0) p[i][j]=0;
                  else if (i==1) p[j][j+i]=a[j];
                  else if (i%2==0) p[j][j+i]=p[j][j+i-1]+p[j+1][j+i]-p[j+1][j+i-1];
                else p[j][j+i]=p[j][j+i-1]+p[j+1][j+i]-p[j+1][j+i-1]+a[(j+j+i)/2];
              }
        }
    for (i=1;i<=n;i++)
      for (k=1;k<=min(i,m);k++)
        if (k==i) dp[i][k]=0;
        else if (k==1) dp[i][k]=p[1][i];
        else
          {
            dp[i][k]=100000000;
            for (j=i-1;j>=k-1;j--)
              dp[i][k]=min(dp[i][k],dp[j][k-1]+p[j+1][i]);    
          }
    cout<<dp[n][m];
}

作为好人,我把pascal,C++都上传,但别盲目复制,仔细体会,大家放心,两个程序都已AC。

 

动态规划(1)

标签:

原文地址:http://www.cnblogs.com/ZZMbk/p/4715218.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!