题目描述
乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过5050。
现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。
给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。
输入输出格式
输入格式:
共二行。
第一行为一个单独的整数N表示砍过以后的小木棍的总数,其中N≤65N≤65
(管理员注:要把超过5050的长度自觉过滤掉,坑了很多人了!)
第二行为NN个用空个隔开的正整数,表示NN根小木棍的长度。
输出格式:
一个数,表示要求的原始木棍的最小可能长度
输入输出样例
说明
2017/08/05
数据时限修改:
-#17 #20 #22 #27 四组数据时限500ms500ms
-#21 #24 #28 #29 #30五组数据时限1000ms1000ms
其他时限改为200ms200ms(请放心食用)
一开始想贪心什么的 肯定是暴力搜索 只管搜就完事了
dfs回溯法
小优化: 27分
#include<bits/stdc++.h> using namespace std; //input by bxd #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);i--) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m) #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s); #define ll long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) // #define inf 214748347 #define N 1500+5 int cnt=0; int sum=0; int n; int a[100]; int vis[100]; int minn; int ok; void dfs(int tot,int t) { if(ok)return ; if(t==0) { ok=1; printf("%d\n",minn); } rep(i,1,cnt) if(!vis[i]) { if(tot+a[i]<minn) { vis[i]=1; dfs(tot+a[i],t); vis[i]=0; } else if(tot+a[i]==minn) { vis[i]=1; dfs(0,t-1); vis[i]=0; } } } int main() { int n; RI(n); rep(i,1,n) { int x; RI(x); if(x<=50) { sum+=x; a[++cnt]=x; } } sort(a+1,a+1+cnt); for(minn=a[cnt];;minn++) { if(sum%minn)continue;//剪枝 n=sum/minn; CLR(vis,0); ok=0; dfs(0,n); if(ok)break; } return 0; }
二分找最后一个点优化:33分
#include<bits/stdc++.h> using namespace std; //input by bxd #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);i--) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m) #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s); #define ll long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) // #define inf 214748347 #define N 1500+5 int cnt=0; int sum=0; int n; int a[100]; int vis[100]; int minn; int ok; void dfs(int tot,int t) { if(ok)return ; if(t==0) { ok=1; printf("%d\n",minn); } rep(i,1,cnt) if(!vis[i]) { if(tot+a[i]==minn) { vis[i]=1; dfs(0,t-1); vis[i]=0; } else if(tot+a[i]<minn) { int b=tot+a[i]; int pos=lower_bound(a+1,a+1+cnt,minn-b)-a; while( a[pos]==minn-b&&vis[pos])pos++; if(a[pos]==minn-b) { vis[i]=1; vis[pos]=1; dfs(0,t-1); vis[i]=0; vis[pos]=0; } else { vis[i]=1; dfs(tot+a[i],t); vis[i]=0; } } } } int main() { int n; RI(n); rep(i,1,n) { int x; RI(x); if(x<=50) { sum+=x; a[++cnt]=x; } } sort(a+1,a+1+cnt); for(minn=a[cnt];;minn++) { if(sum%minn)continue; n=sum/minn; CLR(vis,0); ok=0; dfs(0,n); if(ok)break; } return 0; }
事实证明这个二分没什么用
将大的放在前面更好!!!!!
加一个排序就33了
#include<bits/stdc++.h> using namespace std; //input by bxd #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);i--) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m) #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s); #define ll long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) // #define inf 214748347 #define N 1500+5 int cnt=0; int sum=0; int n; int a[100]; int vis[100]; int minn; int ok; bool cmp(int a,int b) { return a>b; } void dfs(int tot,int t) { if(ok)return ; if(t==0) { ok=1; printf("%d\n",minn); } rep(i,1,cnt) if(!vis[i]) { if(tot+a[i]==minn) { vis[i]=1; dfs(0,t-1); vis[i]=0; } else if(tot+a[i]<minn) { vis[i]=1; dfs(tot+a[i],t); vis[i]=0; } } } int main() { int n; RI(n); rep(i,1,n) { int x; RI(x); if(x<=50) { sum+=x; a[++cnt]=x; } } sort(a+1,a+1+cnt,cmp); for(minn=a[1];;minn++) { if(sum%minn)continue; n=sum/minn; CLR(vis,0); ok=0; dfs(0,n); if(ok)break; } return 0; }
再对 匹配完成进行小优化? 51分 //帮助头匹配很重要
#include<bits/stdc++.h> using namespace std; //input by bxd #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);i--) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m) #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s); #define ll long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) // #define inf 214748347 #define N 1500+5 int cnt=0; int sum=0; int n; int a[100]; int vis[100]; int minn; int ok; bool cmp(int a,int b) { return a>b; } void dfs(int tot,int t,int cur) { if(ok)return ; if(t==0) { ok=1; printf("%d\n",minn); } int d=minn-tot; int L=cur; int R=cnt; rep(i,1,cnt) { if(vis[i])continue; if(tot+a[i]==minn) { vis[i]=1; int j; for( j=1;j<=cnt;j++) if(!vis[j])break; vis[j]=1; dfs(a[j],t-1,i); vis[j]=0; vis[i]=0; } if(tot+a[i]<minn) { vis[i]=1; dfs(tot+a[i],t,i); vis[i]=0; } } } int main() { int n; RI(n); rep(i,1,n) { int x; RI(x); if(x<=50) { sum+=x; a[++cnt]=x; } } sort(a+1,a+1+cnt,cmp); for(minn=a[1];;minn++) { if(sum%minn)continue; n=sum/minn; CLR(vis,0); ok=0; dfs(0,n,1); if(ok)break; } return 0; }
二分优化: 60
#include<bits/stdc++.h> using namespace std; //input by bxd #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);i--) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m) #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s); #define ll long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) // #define inf 214748347 #define N 1500+5 int cnt=0; int sum=0; int n; int a[100]; int vis[100]; int minn; int ok; bool cmp(int a,int b) { return a>b; } void dfs(int tot,int t,int cur) { if(ok)return ; if(tot==minn) { t--; if(t==0) { ok=1;return; } int i; for( i=1;i<=cnt;i++) if(!vis[i])break; vis[i]=1; dfs(a[i],t,i); vis[i]=0; if(ok)return; } int L=cur; int R=cnt; while(L<R) { int mid=(L+R)>>1; if(a[mid]>minn-tot)L=mid+1; else R=mid; } for(int i=L;i<=cnt;i++) { if(vis[i])continue; if(tot+a[i]<=minn) { vis[i]=1; dfs(tot+a[i],t,i); vis[i]=0; } } } int main() { int n; RI(n); rep(i,1,n) { int x; RI(x); if(x<=50) { sum+=x; a[++cnt]=x; } } sort(a+1,a+1+cnt,cmp); for(minn=a[1];;minn++) { if(sum%minn)continue; n=sum/minn; CLR(vis,0); ok=0; vis[1]=1; dfs(a[1],n,1); vis[1]=0; if(ok) { printf("%d\n",minn);break; } } return 0; }
加一条小剪枝:69
#include<bits/stdc++.h> using namespace std; //input by bxd #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);i--) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m) #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s); #define ll long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) // #define inf 214748347 #define N 1500+5 int cnt=0; int sum=0; int n; int a[100]; int vis[100]; int minn; int ok; bool cmp(int a,int b) { return a>b; } void dfs(int tot,int t,int cur) { if(ok)return ; if(tot==minn) { t--; if(t==0) { ok=1;return; } int i; for( i=1;i<=cnt;i++) if(!vis[i])break; vis[i]=1; dfs(a[i],t,i); vis[i]=0; if(ok)return; } int L=cur; int R=cnt; while(L<R) { int mid=(L+R)>>1; if(a[mid]>minn-tot)L=mid+1; else R=mid; } for(int i=L;i<=cnt;i++) { if(vis[i])continue; if(tot+a[i]<=minn) { vis[i]=1; dfs(tot+a[i],t,i); vis[i]=0; if(ok)return ; if(a[i]==minn-tot)return ; } } } int main() { int n; RI(n); rep(i,1,n) { int x; RI(x); if(x<=50) { sum+=x; a[++cnt]=x; } } sort(a+1,a+1+cnt,cmp); for(minn=a[1];;minn++) { if(sum%minn)continue; n=sum/minn; CLR(vis,0); ok=0; vis[1]=1; dfs(a[1],n,1); vis[1]=0; if(ok) { printf("%d\n",minn);break; } } return 0; }
如果当前木棍长度就等于剩余要拼接长度 也就是拼上去正好 那么return (当然 是放在拼接失败后面 也就是dfs后面)
原理:小的比大的更加灵活
比如当前缺5 目标正好是5 (且后面还有2 3(如果后面没有23就更不用说了) ) 此时拼上去剩余2,3 如果不行的话 后面肯定会追溯到拼接(2,3) 剩余5 这样更加失败 !!!!!小的更灵活
加上nex数组 终于ac了QAQ
#include<bits/stdc++.h> using namespace std; //input by bxd #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define repp(i,a,b) for(int i=(a);i>=(b);i--) #define RI(n) scanf("%d",&(n)) #define RII(n,m) scanf("%d%d",&n,&m) #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k) #define RS(s) scanf("%s",s); #define ll long long #define REP(i,N) for(int i=0;i<(N);i++) #define CLR(A,v) memset(A,v,sizeof A) // #define inf 214748347 #define N 1500+5 int cnt=0; int sum=0; int n; int a[100]; int vis[100]; int nex[100]; int minn; int ok; bool cmp(int a,int b) { return a>b; } void dfs(int tot,int t,int cur) { if(ok)return ; if(tot==minn) { t--; if(t==0) { ok=1;return; } int i; for( i=1;i<=cnt;i++) if(!vis[i])break; vis[i]=1; dfs(a[i],t,i); vis[i]=0; if(ok)return; } int L=cur; int R=cnt; while(L<R) { int mid=(L+R)>>1; if(a[mid]>minn-tot)L=mid+1; else R=mid; } for(int i=L;i<=cnt;i++) { if(vis[i])continue; if(tot+a[i]<=minn) { vis[i]=1; dfs(tot+a[i],t,i); vis[i]=0; if(ok)return ; if(a[i]==minn-tot)return ; i=nex[i]; } } } int main() { int n; RI(n); rep(i,1,n) { int x; RI(x); if(x<=50) { sum+=x; a[++cnt]=x; } } sort(a+1,a+1+cnt,cmp); nex[cnt]=cnt; repp(i,cnt-1,1) if(a[i]==a[i+1])nex[i]=nex[i+1]; else nex[i]=i; for(minn=a[1];;minn++) { if(sum%minn)continue; n=sum/minn; ok=0; vis[1]=1; dfs(a[1],n,1); vis[1]=0; if(ok) { printf("%d\n",minn);break; } } return 0; }
巨佬的做法:
用桶 储存
#include<cstdio> #include<cstdlib> const int N = 70 ; int n , cnt , tot , maxn , minn , tm[ N ] /* 2 */ ; void dfs( int res , int sum , int target , int p ) { if( res == 0 ) { printf("%d", target ); exit( 0 ); } if( sum == target ) { dfs( res - 1 , 0 , target , maxn ); return; } for( int i = p ; i >= minn ; i -- ) { // 2 3 if( tm[ i ] && i + sum <= target ) { tm[ i ] -- ; dfs( res , sum + i , target , i ); tm[ i ] ++ ; if ( sum == 0 || sum + i == target ) //4 break; } } return; } int main() { scanf("%d" , &n ) ; minn = N ; int temp; while( n -- ) { scanf("%d" , &temp ); if( temp <= 50 ) { cnt ++; tm[ temp ] ++; tot += temp; maxn = maxn > temp ? maxn : temp ; //1 minn = minn < temp ? minn : temp ; } } temp = tot >> 1; for( int i = maxn ; i <= temp ; i ++ ) { if( tot % i == 0 ) { dfs( tot / i , 0 , i , maxn ); } } printf("%d" , tot ); return 0; }
巨佬2做法:
这个居然没什么优化都能过!
using namespace std; bool cmp(int x,int y) { return x>y; } int n,t,sum=0,cnt=0,a[70],res,vis[70],pp; int dfs(int len,int sta,int now)// dfs(剩下的长度,第几个木棍开始,已经摆好了几组) { if(now==res) return 1; if(len==0) if(dfs(pp,1,now+1))// 当前这一根已经摆完 剩下的长度应该重新开始 return 1; for(int j=sta;j<=cnt;j++) if(!vis[j]&&a[j]<=len) { vis[j]=1; // 保证点未被用过 if(dfs(len-a[j],j+1,now)) // 搜下一个点 return 1; vis[j]=0; if(len==a[j]||len==pp)// 首先:能运行到这表明最大的a[i]也不能满足条件 然后:如果len==pp 即一组新的木棍 那么以后的也不能满足条件 break; // 用了当前木棍后无法拼好 但是剩下的木棍长度还等于当前木棍长度 跳出 while(a[j+1]==a[j]) // 当前长度不行 其他长度也不行 j++; } return 0; } int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&t); if(t<=50) { sum+=t; a[++cnt]=t; } } sort(a+1,a+1+cnt,cmp); for(int i=a[1];i<=sum;i++) if(sum%i==0) { pp=i; // 全局记录长度 res=sum/i; if(dfs(i,1,0)) { printf("%d",i); return 0; } } return 0; }