题意:一共有两个车,容量是c1和c2~100, n~10个物品,都有一个重量,两辆车每次必须一块从起点开到终点,问怎么安排能够使开的次数最少。
题解:问的是使开车的次数最少,考虑最后一次开车把什么带走,枚举,就是装压,那么之前呢?就是根据装压的01背包。首先需要将10个东西的状态判断下可以一次用两辆车拉走的情况预处理出来(这个预处理比较简单,用dp来写),作为之后dp的物品,然后之后就是01背包的dp,求dp的最小值,就是次数。
重点:首先想到最后一次运,然后运什么利用装压枚举,然后枚举想到dp,之后按照一次一次的看,发现需要先预处理。一个思考的过程,敢于想最后一次,敢于去枚举,枚举想dp。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)
typedef long long ll;
using namespace std;
const int maxn = 1e2 + 10;
const int MAX_S = (1<<10) + 100;
const int INF = 100000;
int dp[MAX_S], a[MAX_S], a_n, can[maxn], w[maxn], n, c1, c2;//dp是之后将一次运输看做物品时的dp,can是预处理时用的dp
int key;
bool judge(int s)//判断s状态一次能不能运过去。
{
int tot = 0;
memset(can, -1, sizeof(can));//车1可以运输的最大重量,用dp来搞。
can[0] = 0;
for(int i = 0; i < n; i++)
{
if(((1<<i)&s))
{
tot += w[i];
for(int j = c1; j >= 0; j--)
{
if(can[j] != -1)
{
;
}
else
{
if(j >= w[i]&&can[j-w[i]]!=-1)
{
can[j] = 0;
}
}
}
}
}
for(int i = 0; i <= c1; i++)
{
if(can[i]!=-1&&tot-i<=c2)//判断
return 1;
}
return 0;
}
void getS()//预处理出一次可以搞定的所有情况,作为之后dp的物品。
{
a_n = 0;
key = (1<<n)-1;
for(int s = 0; s <= key; s++)
{
if(judge(s))
{
a[a_n] = s;
a_n++;
}
}
}
void getDp()//01背包但是是装压
{
for(int i = 1; i <= key; i++)
{
dp[i] = INF;
}
dp[0] = 0;
for(int i = 0; i < a_n; i++)
{
for(int j = key; j >= 0; j--)//仍然倒序
{
if((j&a[i])==a[i])//子集
{
int t = j-a[i];
if(dp[t]!=INF&&dp[t]+1<dp[j])
{
dp[j] = dp[t] + 1;
}
}
}
}
}
void solve()
{
getS();
getDp();
printf("%d\n", dp[key]);
}
int main()
{
// freopen("4Din.txt", "r", stdin);
//freopen("4Dout.txt", "w", stdout);
int ncase;
scanf("%d", &ncase);
for(int _ = 1; _ <= ncase; _++)
{
printf("Scenario #%d:\n", _);
scanf("%d%d%d", &n, &c1, &c2);
REP(i, 0, n)
{
scanf("%d", &w[i]);
}
solve();
printf("\n");
}
return 0;
}