流水线调度问题
简单描述
两条流水工作线,问怎样处理一个工件,使花费的时间最少。
题解
简单而典型的动态规划问题,对一个单独的处理站而言,到这个站为止最少的处理时间为 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;
}