原题: https://www.luogu.org/problemnew/show/P1120
题意:
有n(<=65)根小木棍,长度 a i a_i ai(<=50),这些小木棍是由x根L长木棍切割而得,现在x和L不确定,让你求最小的L。
题目说了如果出现大于50的木棍要忽略。
解析:
首先当然是要枚举长度L了,下界为给出长度最大值 m a ma ma,上界为所有木棍长度和 s u m sum sum。当然 s u m sum sum是一定成立的,所以枚举到 s u m / 2 sum/2 sum/2即可。
开始的时候,我有点犯傻了,以为只要数据可以组合,贪心组合即可,用链表存状态的更新情况,用数组维护剩余的棍子,一发牛b哄哄的代码WA了。
#include
using namespace std;
#define debug(i) printf(" # %d\n",i)
int a[66], sum, ma;
bool vis[3255];
struct state {
int pre, num;
} e[3255];
int sta[3255], co;
void Erase(int S[], int &n, int Len) {
stack<int>T;
int now = Len;
while(now > 0) {
T.push(e[now].num);
now = e[now].pre;
}
int j = 0;
for(int i = 1; i <= n; i++) {
if(!T.empty() && i == T.top()) {
T.pop();
continue;
}
S[++j] = S[i];
}
n = j;
}
bool cmp(int a, int b) {
return a > b;
}
int main() {
int n;
scanf("%d", &n);
sum = 0;
ma = -1;
for(int i = 1; i <= n; i++) {
int tmp;
scanf("%d", &tmp);
if(tmp > 50) {
n--;
i--;
continue;
}
a[i] = tmp;
sum += tmp;
ma = max(ma, tmp);
}
sort(a + 1, a + 1 + n, cmp);
int ans = sum;
for(int Len = ma; Len <= sum / 2; Len++) { // 枚举长度
debug(Len);
if(sum % Len)
continue;
int num = sum / Len; // 段数
int S[66]; // 存剩下的棍子
int top = n;
for(int i = 1; i <= n; i++)
S[i] = a[i];
bool cant = 0;
while(num--) {
bool succ = 0;
OUT(S, top);
memset(vis, 0, sizeof(vis));
co = 1;
vis[0] = 1;
sta[1] = 0;
for(int i = 1; i <= top; i++) {
int nowco = co;
for(int j = 1; j <= nowco; j++) {
int to = sta[j] + S[i];
if(to > Len || vis[to])
continue;
// to这个状态由sta[j]加上剩下数组中第i个棍子得到
e[to].num = i;
e[to].pre = sta[j];
sta[++co] = to;
vis[to] = 1;
if(to == Len) {
Erase(S, top, Len);
succ = 1;
break;
}
}
if(succ)
break;
}
if(!succ) {
cant = 1;
break;
}
}
if(!cant) {
ans = Len;
break;
}
}
printf("%d\n", ans);
}
后来猛地想起来,选取策略是有影响的,比如用 3 3 3 3 1111 3 \,3\, 3\, 3\, 1 1 1 1 33331111来拼4个4,如果先选1111,那么就不能拼了。
所以只能暴搜了,dfs(int Len, int needLen, int needNum, int up)
表示设置原长度为Len,当前棍子还需要拼needLen,除了当前棍子还需要拼needNum根,下一个选取来凑的棍子从up开始往下。
说说需要注意的事:
a[Len]--
就可以维护剩余木棍。needLen==Len
,那么选完后就return,因为这个木棍一定会被选。即第一个选什么并不重要。#include
using namespace std;
int a[100], n;
int ma, mi, sum;
void dfs(int Len, int needLen, int needNum, int up) {
if(needNum == 0) {
printf("%d\n", Len);
exit(0);
}
if(needLen == 0) {
dfs(Len, Len, needNum - 1, ma);
return;
}
for(int i = min(up, needLen); i >= mi; i--) {
if(a[i]) {
a[i]--;
dfs(Len, needLen - i, needNum, i);
a[i]++;
if(needLen == Len || needLen == i)
return ;
}
}
}
int main() {
memset(a, 0, sizeof(a));
scanf("%d", &n);
mi = 1e9, ma = -1, sum = 0;
for(int i = 1; i <= n; i++) {
int tmp;
scanf("%d", &tmp);
if(tmp > 50)
continue;
a[tmp]++;
ma = max(ma, tmp);
mi = min(mi, tmp);
sum += tmp;
}
int ans = sum;
for(int i = ma; i <= sum / 2; i++) {
if(sum % i)
continue;
dfs(i, i, sum / i, ma);
}
printf("%d\n", ans);
}