动态规划

来源:互联网 发布:老虎伊甸园 知乎 编辑:程序博客网 时间:2024/06/05 10:04

流水线调度问题

简单描述

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

题解

简单而典型的动态规划问题,对一个单独的处理站而言,到这个站为止最少的处理时间为 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;  }
0 0
原创粉丝点击