今天练习了一些搜索,动态规划的题,只敢选普及-的来做,这个题看上去感觉用贪心来做,而且看上去不知道怎么用背包解题,他的最大“容量”是不确定的。
题目是这样的:
【题目背景】
kkksc03 的大学生活非常的颓废,平时根本不学习。但是,临近期末考试,他必须要开始抱佛脚,以求不挂科。
【题目描述】
这次期末考试,kkksc03 需要考 4 科。因此要开始刷习题集,每科都有一个习题集,分别有 s1,s2,s3,s4 道题目,完成每道题目需要一些时间,可能不等(A1,A2,…,As1,B1,B2,…,Bs2,C1,C2,…,Cs3,D1,D2,…,Ds4)。
kkksc03 有一个能力,他的左右两个大脑可以同时计算 2 道不同的题目,但是仅限于同一科。因此,kkksc03 必须一科一科的复习。
由于 kkksc03 还急着去处理洛谷的 bug,因此他希望尽快把事情做完,所以他希望知道能够完成复习的最短时间。
【输入】
本题包含 5 行数据:第 11 行,为四个正整数 s1,s2,s3,s4。
第 2 行,为 A1,A2,…,As1 共 s1 个数,表示第一科习题集每道题目所消耗的时间。
第 3 行,为 B1,B2,…,Bs2 共 s2 个数。
第 4 行,为 C1,C2,…,Cs3 共 s3 个数。
第 5 行,为 D1,D2,…,Ds4 共 s4 个数,意思均同上。
【输出】
输出一行,为复习完毕最短时间。
样例输入
1 2 1 3 5 4 3 6 2 4 3
样例输出
20
解题思路
这个题分为四组来看,因为题目中:“他的左右两个大脑可以同时计算 2 道不同的题目,但是仅限于同一科。”这四组分别是四个科目。
然后我想的是在同一个科目中,把两个数拿出来比较,比较 n-1 次,记录下每一次之差,第一个差为数组第一个数,比如如果是对于 A 科目,用sum记录最短时间,第一个数是 A[0],计数器sum=sum+A[0],令 k=A[0],把 k 与 A[1] 比较,若A[1]>k, k则重新赋值为 k=A[i]-k,计数器 sum=sum+k;若 A[1]<k,k=k-A[1],计数器不增加,之后的同理。
我运用的贪心的思想,但是这样写出来的代码全是WA,代码如下:
#include<stdio.h>
int main()
{
int i,a,b,c,d,sum=0,k;
int A[25],B[25],C[25],D[25];
//输入每个科目的题数
scanf("%d %d %d %d",&a,&b,&c,&d);
//输入每个科目花费的时间
for(i=0;i<a;i++)
scanf("%d",&A[i]);
for(i=0;i<b;i++)
scanf("%d",&B[i]);
for(i=0;i<c;i++)
scanf("%d",&C[i]);
for(i=0;i<d;i++)
scanf("%d",&D[i]);
//分别计算出每个科目花费的最短时间
k=A[0];
sum=sum+A[0];
for(i=1;i<a;i++)
{
if(A[i]>=k)
k=A[i]-k;
else if(A[i]<k)
k=k-A[i];
sum=sum+k;
}
k=B[0];
sum=sum+B[0];
for(i=1;i<b;i++)
{
if(B[i]>=k)
{
k=B[i]-k;
sum=sum+k;
}
else if(B[i]<k)
k=k-B[i];
}
k=C[0];
sum=sum+C[0];
for(i=1;i<c;i++)
{
if(C[i]>=k)
{
k=C[i]-k;
sum=sum+k;
}
else if(C[i]<k)
k=k-C[i];
}
k=D[0];
sum=sum+D[0];
for(i=1;i<d;i++)
{
if(D[i]>=k)
{
k=D[i]-k;
sum=sum+k;
}
else if(D[i]<k)
k=k-D[i];
}
printf("%d\n",sum);
return 0;
}
至于哪里出问题了,应该是数据过大时计算会出问题。
可以试试背包解题,先要解决背包的最大“容量”是多大,再看看题目,如果每个题消耗的时间相加为 sum,这个最短时间 time 是不是一定会大于等于 sum/2 ?因为最凑巧的情况是两个题消耗的时间一样(两个都为 5),sum为 10,这样最短时间 time 正好为 sum/2(5),但是不会出现比 sum/2 更小的情况了。
反过来消耗时间 sum 减去前面所说的最短时间 time,这个 sum-time 是不是就一定小于等于 sum/2 了,这样看来就可以试试用背包求 sum-time,此时背包最大容积为 sum/2,然后要求的答案为:用背包求解得出的,四个dp[sum/2][n]之和。
代码如下:
#include<stdio.h>
int dp[1205][25],sum,k[4],a[25];
//定义全局的 dp 数组和 a 数组
int max(int x,int y)
{
if(x>=y)
return x;
else
return y;
}
//用动态规划求解
void fun(int v,int n)
{
int i,j;
//一维遍历题目个数
for(j=1;j<=n;j++)
{
//二维遍历总和时间的一半,因为这里求的dp最大不会超过sum/2
for(i=1;i<=v;i++)
{
if(i>=a[j])
dp[i][j]=max(dp[i][j-1],dp[i-a[j]][j-1]+a[j]);
else
dp[i][j]=dp[i][j-1];
}
}
}
int main()
{
int t=0,i,j,b;
for(i=0;i<4;i++)
scanf("%d",&k[i]);
for(j=0;j<4;j++)
{
sum=0;
for(i=1;i<=k[j];i++)
{
scanf("%d",&a[i]);
sum=sum+a[i];
}
fun(sum/2,k[j]);
//每一科目所需的最短时间为sum-dp[sum/2][k[j]]
t=t+sum-dp[sum/2][k[j]];
//在一个科目计算完后,要归零,否则会影响后面科目的计算
for(i=0;i<=sum;i++)
{
for(b=0;b<=k[j];b++)
dp[i][b]=0;
}
}
printf("%d\n",t);
return 0;
}
总结
用动态规划还是很不熟悉,做这个题目时,把dp数组的一维和二维弄混了,然后出错了,虽然思路基本正确,但是代码没有实现正确,还是得多做有关这个的题目。