P1460 健康的荷斯坦奶牛 Healthy Holsteins
题目描述
农民JOHN以拥有世界上最健康的奶牛为傲。他知道每种饲料中所包含的牛所需的最低的维他命量是多少。请你帮助农夫喂养他的牛,以保持它们的健康,使喂给牛的饲料的种数最少。
给出牛所需的最低的维他命量,输出喂给牛需要哪些种类的饲料,且所需的饲料剂量最少。
维他命量以整数表示,每种饲料最多只能对牛使用一次,数据保证存在解。
输入输出格式
输入格式:第1行:一个整数V(1<=V<=25),表示需要的维他命的种类数。
第2行:V个整数(1<=每个数<=1000),表示牛每天需要的每种维他命的最小量。
第3行:一个整数G(1<=G<=15),表示可用来喂牛的饲料的种数。
下面G行,第n行表示编号为n饲料包含的各种维他命的量的多少。
输出格式:输出文件只有一行,包括
牛必需的最小的饲料种数P
后面有P个数,表示所选择的饲料编号(按从小到大排列)。
如果有多个解,输出饲料序号最小的(即字典序最小)。
输入输出样例
输入样例#1:
4 100 200 300 400 3 50 50 50 50 200 300 200 300 900 150 389 399
输出样例#1:
2 1 3
说明
USACO 2.1
翻译来自NOCOW
被卡了一个小时,第一次写剪纸的蒟蒻就是我。
首先就是要枚举不重复的子集,根据测算,只需要枚举25 +24 +23 +...+1 = (25 + 1)*25/2个子集,所以时间复杂度是相当低的。
但是如果枚举成25!的子集,一定会崩掉。
我们为了避免枚举重复的子集,采取了这样的枚举自己方法(紫书里还有一种):
枚举子集内元素个数1..n,并保证子集是单调递增的。这样就能够补充不漏的枚举子集。
上代码。
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <algorithm>
const int MAXN = 25 + 15;
const int MAXM = 15 + 15;
int n;
int min[MAXN];
int m;
int value[MAXM][MAXN];
int c[MAXN];
int num[MAXN];
bool b[MAXM];
bool IsFull[MAXN];
int ans;
int cnt;
bool ok;
void dfs(int step,int pre)
{
if(ok)return;
if(step > cnt + 1) return;
if(step == cnt + 1)
{
for(int i = 1;i <= n;i ++)
{
int sum = 0;
for(int j = 1;j <= cnt;j ++)
{
sum += value[num[j]][i];
}
if(sum < min[i])return;
}
ans = cnt;
ok = true;
return;
}
for(int i = 1;i <= m;i ++)
{
if(ok)return;
if(i <= pre)continue;
if(!b[i])
{
num[step] = i;
b[i] = true;
dfs(step + 1,i);
b[i] = false;
}
}
}
int main()
{
freopen("data.txt", "r", stdin);
ans = 99999999;
scanf("%d", &n);
for(int i = 1;i <= n;i ++)
{
scanf("%d", &min[i]);
}
scanf("%d", &m);
for(int i = 1;i <= m;i ++)
{
for(int j = 1;j <= n;j ++)
{
scanf("%d", &value[i][j]);
}
}
for(int i = 1;i <= m;i ++)
{
cnt ++;
dfs(1,0);
if(ok) break;
}
printf("%d ", ans);
for(int i = 1;i <= ans;i ++)
{
printf("%d ", num[i]);
}
return 0;
}