动态规划

流水线调度问题

简单描述

这里写图片描述
两条流水工作线,问怎样处理一个工件,使花费的时间最少。

题解

简单而典型的动态规划问题,对一个单独的处理站而言,到这个站为止最少的处理时间为 f1[n]=min(f1[n-1]+cost1[n],f2[n-1]+waycost[n-1]+cost1[n]),(waycost指的是中途从二号线转到一号线所花费的时间,cost1[n]指的是在一号线上第n个处理站处理这个工件所花费的时间)。
同理处理二号线得到f2[n],最后下线取两条线上花费时间的较小值。

代码

因为没有找到OJ上的具体题目,只好自己大概码了下思路。

#include<bits/stdc++.h>
using namespace std;

int linea[100];//stationµÄ»¨·Ñ
int lineb[100];
int atob[100];
int btoa[100];
int disa[100];
int disb[100];
int intoa,intob,offa,offb;

int main()
{
    intoa=2;intob=4;offa=3;offb=2;
    for(int i=1;i<=6;i++)
    {
        scanf("%d%d%d%d",&linea[i],&atob[i],&btoa[i],&lineb[i]);
    }
    disa[1]=9;
    disb[1]=12;
    for(int i=2;i<=6;i++)
    {
        disa[i]=min(disa[i-1]+linea[i],disb[i-1]+btoa[i-1]+linea[i]);
        disb[i]=min(disb[i-1]+lineb[i],disa[i-1]+atob[i-1]+lineb[i]);

    }
    disa[7]=disa[6]+offa;
    disb[7]=disb[6]+offb;
    int ans=min(disa[7],disb[7]);
    cout<<ans<<endl;
    return 0;
}

HDU1003 计算最大区间和

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=1003

题解

我感觉我这不算动规。。就是一个贪心,只要前一个字段的和是大于1的就把当前的数字加入其中,否则就舍弃前面所有的,把当前位置作为新的子段起点,然后最后在扫描一下,找到最大值。

代码

#include<iostream>
#include<queue>
  #include<cstdio>
  #include<cstring>
  #include<string>
  #include<cmath>
  #include<string>
 #include<cctype>
  #include<algorithm>
  #include<vector>

  using namespace std;
  typedef pair<int,int> p;
  ;
  int main()
  {
      int t;
      scanf("%d",&t);
      int kkk=0;
      while(t--)
      {   kkk++;
          p a[100005];
          int b[100005];
          int ans[100005];
          int n;
          scanf("%d",&n);
          for(int i=1;i<=n;i++)
          {
              scanf("%d",&b[i]);
          }
          a[1].first=1;
          a[1].second=1;
          ans[1]=b[1];
          for(int i=2;i<=n;i++)
          {
              if(ans[i-1]+b[i]>=b[i])
              {
                  ans[i]=ans[i-1]+b[i];
                  a[i].first=a[i-1].first;
                  a[i].second=i;
              }
              else{
                ans[i]=b[i];
                a[i].first=i;
                a[i].second=i;
              }
          }
          int maxn=-1e9-10;
          int op;
          for(int i=1;i<=n;i++)
          {
              if(ans[i]>maxn)
              {
                  maxn=ans[i];
                  op=i;
              }
          }
          printf("Case %d:\n",kkk);
          printf("%d %d %d\n",maxn,a[op].first,a[op].second);
         if(t!=0) printf("\n");
      }
      return 0;
  }

HDU1466 计算直线的交点数

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=1466

题解

多条直线相交,问所有可能的交点情况。首先,我们可以把问题简化为,开始是m个点,ans[m]=∑(r=0,r=m) (m-r)*r+ans[r]。m-r条平行直线,单独与r条直线的交点数+r条直线相互之间形成的交点数。每个答案是一种情况,依次列出所有情况。

代码

#include<iostream>
#include<queue>
  #include<cstdio>
  #include<cstring>
  #include<string>
  #include<cmath>
  #include<string>
 #include<cctype>
  #include<algorithm>
  #include<vector>
  #include<set>
#include<bits/stdc++.h>
  using namespace std;
int main()
{
    set<int> s[24];
    s[0].insert(0);
    s[1].insert(0);
    s[2].insert(0);
    s[2].insert(1);
    for(int i=3;i<=20;i++)
    {
        for(int j=0;j<i;j++)
        {   set<int>::iterator it=s[j].begin();
            for(;it!=s[j].end();it++)
            {
                int t=(i-j)*j+*it;
                s[i].insert(t);
            }
        }
    }
    int m;
     while(scanf("%d",&m)!=EOF)
      {   set<int>::iterator it;
          it=s[m].begin();
          for(;it!=s[m].end();it++)
          {
              printf("%d",*it);
              set<int>::iterator it2=s[m].end();
              it2--;
              if(it!=it2)
              {
                  printf(" ");
              }
          }
          printf("\n");
      }
    return 0;
}

HDU1087 棋子跳跃

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=1087

题解

最大升序子列的和问题,每次检查i之前的所有棋子,与当前的值比较取较大值,最后在扫描一下找出最大的值。

代码

#include<iostream>
#include<queue>
  #include<cstdio>
  #include<cstring>
  #include<string>
  #include<cmath>
  #include<string>
 #include<cctype>
  #include<algorithm>
  #include<vector>
  #include<set>
  using namespace std;


  int main()
  {
      int n;
      while(scanf("%d",&n)!=EOF)
      {
          if(n==0)break;
          int a[1005];
          int ans[1005];
          for(int i=0;i<n;i++)
          {
              scanf("%d",&a[i]);
              ans[i]=a[i];
          }
          for(int i=1;i<n;i++)
          {
              for(int j=i;j>=0;j--)
              {
                  if(a[j]<a[i]){ans[i]=max(ans[j]+a[i],ans[i]);}
              }
          }
          int maxn=-1;
          for(int i=0;i<n;i++)
          {
              if(ans[i]>maxn)maxn=ans[i];
          }
          printf("%d\n",maxn);
      }
      return 0;
  }

HDU1159 最长公共子序列

题意

给出两个串,找出他们最长公共子序列的长度。

题解

状态转移方程:
a[i][j]=max(a[i-1][j],a[i][j-1]) (string1[i]!=string2[j])
a[i][j]=a[i-1][j-1]+1 (string1[i]==string2[j])

代码

#include<iostream>
#include<queue>
  #include<cstdio>
  #include<cstring>
  #include<string>
  #include<cmath>
  #include<string>
 #include<cctype>
  #include<algorithm>
  #include<vector>
  #include<set>
  const int maxn=200000;
  using namespace std;
int a[10000][10000];
int main()
{   char st1[10000];
    char st2[10000];
    while(scanf("%s%s",st1+1,st2+1)!=EOF)
    {       int n1=strlen(st1+1);
            int n2=strlen(st2+1);
            for(int i=0;i<=n1;i++)
                for(int j=0;j<=n2;j++)a[i][j]=0;
            for(int i=1;i<=n1;i++)
            {
                for(int j=1;j<=n2;j++)
                {int t=max(a[i-1][j],a[i][j-1]);
                    if(st1[i]==st2[j])
                    {

                        a[i][j]=a[i-1][j-1]+1;

                    }
                    else a[i][j]=t;
                }
            }
            cout<<a[n1][n2]<<endl;
    }
    return 0;
}

HDU1176 免费馅饼

题意

如题。

题解

可以看出一数塔问题,从下往上考虑,最后输出dp[0][5]位置,即最上面的开始的值。

代码

#include<iostream>
#include<queue>
  #include<cstdio>
  #include<cstring>
  #include<string>
  #include<cmath>
  #include<string>
 #include<cctype>
  #include<algorithm>
  #include<vector>

  using namespace std;

  const int maxn=100005;
  //typedef pair<int,int> p;
  long long dp[maxn][15];
  int main()
  {   int n;
      while(scanf("%d",&n)!=EOF)
      {
          if(n==0)break;
          for(int i=0;i<=maxn;i++)
            for(int j=0;j<15;j++)dp[i][j]=0;
           // vector<p>s;
            for(int i=0;i<n;i++)
            {
                int t,tt;
                scanf("%d%d",&t,&tt);
                dp[tt][t+1]++;
            }
            long long ans=-1;

            for(int i=100002;i>=0;i--)
            {
                for(int j=1;j<=14;j++)
                {
                    dp[i][j]=max(dp[i+1][j-1],max(dp[i+1][j],dp[i+1][j+1]))+dp[i][j];
                   // if(dp[i][j]>ans)ans=dp[i][j];
                }
            }
            printf("%lld\n",dp[0][6]);
      }
      return 0;
  }

HDU1069 Monkey and Banana

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=1069

题意

用立方体尽可能地搭出更高的高度,要求上面的立方体的长宽,都严格小于下面的立方体的长宽。

题解

把一个立方体的6个状态都单独存入,相当于6个立方体一样,然后对底面积排序,然后if (s[j].x>s[i].x&&s[j].y>s[i].y)dp[j]=max(dp[j],dp[i]+w[j]),dp[i]为高度。

代码

#include<iostream>
#include<queue>
  #include<cstdio>
  #include<cstring>
  #include<string>
  #include<cmath>
  #include<string>
 #include<cctype>
  #include<algorithm>
  #include<vector>

  using namespace std;
  typedef struct qz
   {
        int x,y,z;


  };

  bool cmp(const qz &a,const qz &b)
  {
      return a.x*a.y<b.x*b.y;
  }
  int main()
  {
      int n;
      int ii=0;
      while(scanf("%d",&n)!=EOF)
      {   if(n==0)break;
          ii++;
          qz s[200];
          int dp[200];
          memset(dp,0,sizeof(dp));
          int m=0;
          for(int i=0;i<n;i++)
          {
              int a,b,c;
              scanf("%d%d%d",&a,&b,&c);
              s[m].x=a;s[m].y=b;s[m].z=c;m++;
              s[m].x=a;s[m].y=c;s[m].z=b;m++;
              s[m].x=b;s[m].y=a;s[m].z=c;m++;
              s[m].x=b;s[m].y=c;s[m].z=a;m++;
              s[m].x=c;s[m].y=a;s[m].z=b;m++;
              s[m].x=c;s[m].y=b;s[m].z=a;m++;//存入6种状态
          }
          int ans=0;
          sort(s,s+m,cmp);
          for(int i=0;i<m;i++) //依次遍历所有的状态
          {    dp[i]=s[i].z;
              for(int j=0;j<i;j++)
              {
                  if(s[i].x>s[j].x&&s[i].y>s[j].y)
                  {
                      dp[i]=max(dp[i],dp[j]+s[i].z);

                  }
                  ans=max(ans,dp[i]);
              }
          }
          printf("Case %d: maximum height = %d\n",ii,ans);
      }
      return 0;
  }

HDU1158 Employment Planning

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=1158

题意

在每个月,完成一项工程至少需要一些人,雇佣一个人,解雇一个人,聘用一个人都需要花费一定的钱数,问怎样可以使花费的钱最少。

题解

需要注意的是,每个月完成工程师至少需要这么多人,而不是只能雇佣这么多人,可以无条件养着很多人,如果解聘一个人花费的钱特别多的话。我们可以注意到,每个月的花费是与雇佣的工人数息息相关的,而这又不单单只受前一个月完成工程人数的影响,可能与很多个月以前工作的人数有关,那么这样我们可以把工作雇佣的人数作为dp的标准。
for从每个月完成工程的最少人数到,需要人数最多的月的人数(就是至少需要最多工人才能完成任务的那个月的人数)。
cost=要求人数* salary+hire *(k-j):人不够 ,要聘用一些人
cost=要求人数* salary+fire *(k-j) :人多了,要解雇一下人
dp[i][j]=min(dp[i][j],dp[i-1][k]+cost)

代码

#include<iostream>
#include<queue>
  #include<cstdio>
  #include<cstring>
  #include<string>
  #include<cmath>
  #include<string>
 #include<cctype>
  #include<algorithm>
  #include<vector>
#define INF 0x3f3f3f3f
  using namespace std;

  int main()
  {
      int n;
      while(scanf("%d",&n)!=EOF)
      {   if(n==0)break;

          long long hire,sal,fire;
          scanf("%I64d%I64d%I64d",&hire,&sal,&fire);
          int a[15];
          long long dp[15][10000];
          int tot=-1;
          for(int i=0;i<n;i++){scanf("%d",&a[i]);tot=max(tot,a[i]);}
          for(int i=a[0];i<=tot;i++)dp[0][i]=(sal+hire)*i;
         // int ans=INF;
          for(int i=1;i<n;i++)
          {
              for(int j=a[i];j<=tot;j++)
              {   dp[i][j]=INF;
                  for(int k=a[i-1];k<=tot;k++)
                  {
                      long long cost;
                      if(j>=k){cost=sal*j+hire*(j-k);}
                      else {cost=sal*j+fire*(k-j);
                         //   int cost2=k*sal;
                           // cost=min(cost,cost2);

                        }
                      dp[i][j]=min(dp[i][j],dp[i-1][k]+cost);//维持某个人数,但要使花费的钱最少
                  }
              }
          }
          long long ans=INF;
          for(int i=a[n-1];i<=tot;i++)
          {
              ans=min(ans,dp[n-1][i]);
          }
          printf("%I64d\n",ans);
      }
      return 0;
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值