1 : | 5 | ||
---|---|---|---|
2 : | 4 | 3 | |
3: | 6 | ||
4: | 2 | 4 | 3 |
回溯
博主:https://glgjssy-qyhfbqz.blog.luogu.org/solution-p2392
数据范围20?emmm
于是放心大胆地2^n枚举了
枚举每道题交给哪边的脑子解决,找到两边时间较大值的最小值
于是愉快地解决了……剪枝都不用放
#include<iostream>
using namespace std;
int Left,Right,minn,ans;
int s[5];//存科目的数量
int a[21][5];//存科目题目的时间
void search(int x,int y){//从第 x 题开始,第 y 个学科
if(x>s[y]) //题目大于学科题目总数 //循环结束
{
minn=min(minn,max(Left,Right));
return;
}
Left+=a[x][y];
search(x+1,y);
Left-=a[x][y];
Right+=a[x][y];
search(x+1,y);
Right-=a[x][y];//毫无技巧的搜索回溯
}
int main(){
cin>>s[1]>>s[2]>>s[3]>>s[4];//4科的数量
for(int i=1;i<=4;i++)
{
Left=Right=0;//左脑右脑都为0
minn=19260817;//最小值
for(int j=1;j<=s[i];j++)//便利每科的耗时
cin>>a[j][i];//科目i的第j个题的耗时
search(1,i);//一科一科的复习,从第一个题开始,第i个学科
ans+=minn;
}
cout<<ans;
return 0;
}
01背包
博主:https://www.luogu.com.cn/blog/acxblogs/solution-p2392
四个科目其实就是四组数据。考虑一个科目单独分析。
显然若当前要处理的问题的时间和为t,最佳答案是t/2,也就是两边脑所耗时间差为0。
于是现在要求的问题就是使得两边脑所耗时间差尽量小。
于是我想到了二分,胡乱分析一番,想不出来先放一边。进一步分析。
如果只考虑一边脑耗时,由于两边脑是对称的,我们假设这个耗时肯定比t/2小。那么我们要使这个耗时尽量接近t/2。
然后又想到了背包。保证比t/2小,又要使取得的价值尽量大,可以转化为一个费用与价值相同的背包问题。背包大小为t/2,一个题目的费用和价值相等,都是所耗的时间。
假设背包求得的最大价值为v,显然另一个脑耗时为t-v,则总耗时为max(v,t-v)。
四组数据按照相同方法处理。
对于一道题只有两个状态,一是加到左脑,二是加到右脑,所以是01背包
有N件物品和一个容量为V的背包。第i件物品的体积是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大
for (i = 1; i <= n; i++)
for (j = v; j >= c[i]; j--)//在这里,背包放入物品后,容量不断的减少,直到再也放不进了
f[i][j] = max(f[i - 1][j], f[i - 1][j - c[i]] + w[i]);
#include<bits/stdc++.h>
using namespace std;
int len[4];//存每科题目总数 从0开始
int sub[30];//每题的时间 从1开始
int f[21][1201];
int main(){
for(int i=0;i<4;i++)cin>>len[i];
int tot=0;//所有学科总时间
for(int i=0;i<4;i++){//遍历学科
int v=0;//单个学科总时间
for(int j=1;j<=len[i];j++) cin>>sub[j],v+=sub[j];
sort(sub,sub+len[i]);//每题时间 从小到大排序
int t1=0;
//01背包: 个数 容量
for(int j=1;j<=len[i];j++)//j题号
for(int k=0;k<=v/2;k++){//半个背包的容量
// f[j][k]=max(f[j-1][k],f[j-1][k-sub[j]]+sub[j]);
f[j][k]=f[j-1][k];
if(k>=sub[j])
f[j][k]=max(f[j][k],f[j-1][k-sub[j]]+sub[j]);
t1=max(f[j][k],t1);
}
tot+=max(t1,v-t1);
}
cout<<tot<<endl;
return 0;
}