洛谷 P1120 小木棍 題解
阿新 • • 發佈:2019-01-23
這就是一道明顯的爆搜題(人家洛谷也說了,,,),只是需要幾個小小的優化,接下來為大家介紹一下:
1、將木棍先排個序(用處後面會講),記住 長度>50 的要去掉
2、因為每一根的長度都不大於50,所以可以用桶裝,既省空間也省時間
3、……先上程式碼吧,要不然直接講比較抽象(其實是我口才不好)……
————————————(華麗的分割線)————————————————
#include <cstdio>#include <cstdlib>
#include <cstring>
int n=0,m;
int a[110];//桶
int p=0;//木棍總長度
int max=0,min=51;//最長的木棍的長度以及最短的木棍的長度
int ans;//記錄當前成功拼接了幾根木棍,在dfs中用到
void dfs(int x,int y,int z)//x表示每根木棍的期望長度,y表示現在拼成的長度,z表示上一次使用的木棍的長度
{
if(ans*x==p)//假如拼接成功
{
printf("%d",x);
exit(0);//直接結束,此時一定是最優解,因為他要的是最小的解
//而我們是從小到大列舉的,第一個解必然是最優解
}
for(int i=z;i>=min;i--)//因為較長的木棍更難拼,所以從長的先開始
{
if(a[i]&&y+i<=x)//假如還有長度為i的木棍以及拼接起來不會超過期望長度的話
{
y+=i;
if(y==x)ans++,y=0;
a[i]--;
if(y==0)dfs(x,y,max);//如果拼接成功,則z要從max開始
else dfs(x,y,i);
a[i]++;
if(ans>0&&y==0)y=x-i,ans--;
else y-=i;
if(y==0)break;//最重要的優化1:見下文
if(y+i==x)break;//最重要的優化2:同上
}
}
}
int main()
{
scanf("%d",&m);
memset(a,0,sizeof(a));//一定要記得初始化!!!
for(int i=1;i<=m;i++)
{
int x;
scanf("%d",&x);
if(x<=50)//過濾掉超過50的
{
a[x]++,p+=x;
max=max<x?x:max;
min=min>x?x:min;
}
}
for(int i=max;i<=p/2;i++)//列舉原本木棍的長度,用dfs嘗試是否能拼接成功
//由於原本木棍的長度不可能比最長的木棍長度要短,所以從max開始
//以及原本木棍的長度一定能整除總長度(商為1的情況下面會處理),所以假如i>p/2的時候
//i一定不能整除p,所以沒必要dfs,直接跳過
{
if(p%i==0)//如果不能整除則跳過(不能整除則說明會有餘數,也就是多餘的木棍)
{
ans=0;//一定要記得初始化!!!
dfs(i,0,max);
}
}
printf("%d",p);//假如上面未成功,就只能把全部拼在一起了
}
最重要的優化1:首先要思考,什麼時候y==0,當然是剛開始拼接新的一根木棍時,而這條語句在dfs下面,也就是說拼接沒有成功,才會到達這裡,也就剩下那麼些木棍,如果既然拼接都不成功了,那麼也就沒有必要嘗試其他的了,於是break
優化2:上面y==0 break之後,就會到達上一根木棍的最後拼接的時候,那前面都知道是不可以的了,那也沒有必要繼續了,於是也break
以上就是我的想法,如果有人有更好的優化的話,歡迎評論