1615:【例 1】序列的第 k 個數
時間限制: 1000 ms 內存限制: 524288 KB
提交數: 2066 通過數: 619
【題目描述】
BSNY 在學等差數列和等比數列,當已知前三項時,就可以知道是等差數列還是等比數列。現在給你序列的前三項,這個序列要麼是等差序列,要麼是等比序列,你能求出第 k 項的值嗎。 如果第 k 項的值太大,對 200907 取模。
【輸入】
第一行一個整數 T,表示有 T 組測試數據;
對於每組測試數據,輸入前三項 a,b,c,然後輸入 k。
【輸出】
對於每組數據輸出第 k 項的值,對 200907 取模。
【輸入樣例】
2
1 2 3 5
1 2 4 5
【輸出樣例】
5
16
【提示】
樣例說明:
第一組是等差序列,第二組是等比數列。
數據範圍與提示:
對於全部數據,1≤T≤100,1≤a≤b≤c≤109,1≤k≤109 。
思路:
1.設所有木棍長度和爲maxn,那麼原長度(也就是需要輸出的長度)一定能夠被maxn整除,這樣得到的木棍根數纔是整數
2.木棍原來的長度一定不小於所有木棍中最長的那根
綜上兩點,可以確定原木棍的長度len在最長木棍的長度minx和maxn之間取值,且maxn能被len整除。所以在搜索原木棍的長度時,可以從砍過以後所有木棍中最長的長度開始,每次增加長度後,必須能整除maxn。
1.短木棍更加靈活,長木棍受到的限制更大,所以可以對輸入的所有木棍按長度從大到小排序。
2.在砍斷後的排好序的木棍中,當用木棍 i 拼合原始木棍時,可以從i+1的木棍開始往後搜,因爲i前面的木棍已經用過了
3.從當前最長的木棍開始搜,如果拼不出當前設定的原木棍長度len則直接返回,換一個原始木棍長度len
4.相同長度的木棍不要搜索多次.用當前長度的木棍搜下去得不出結果時,用一支同樣長度的還是得不到結果,所以可以提前返回
5.判斷搜到的幾根木棍組成的長度是否大於原始長度len,如果大於沒必要搜下去,可以提前返回
6.判斷當前剩下的木棍根數是否夠拼成木棍,如果不夠,肯定拼合不成功,直接返回
7.找到結果後,在能返回的地方馬上返回到上一層的遞歸處
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,t,a[70],cnt,minx,maxn,len,nxt[70],m;
//a[i]是用來記錄每個標號(cnt)所代表的數 //minx和maxn分別表示下限和上限 //len表示的是我們的區間中的值
//nxt[i]表示的就是記錄當前相同的數的版塊 //m表示我們預估的這個原來的根數
bool vis[70],flag1;
bool cmp(int x,int y)
{
return x>y;
}
void dfs(int k,int last,int res)
{
//k表示當前木棍編號,last爲正在拼的木棍的前一節編號,res爲還需長度
if(res==0)//還需長度爲0,證明拼完了
{
if(k==m)//m根木棍都拼好了
{
flag1=true;//記錄爲成功,也就是找到了一個可以執行目標
return;//返回這個答案到上一個遞歸
}
int i;
for(i = 1;i <= cnt;i++)//又找到一個還沒用過的木棍
if(vis[i]==false) break;//如果我們判斷這是沒用過的就退出這個循環,進入到下面
vis[i] = true;
dfs(k+1,i,len-a[i]);//用它拼接
vis[i] = false;
if(flag1 == 1) return;//找到答案就可以層層退出
}
int l = last+1,r = cnt,mid;
while(l < r)//二分查找下一次拼接的木棍,該木棍是不大於所需長度的第一根木棍
{
mid=(l+r)>>1;
if(a[mid] <= res) r=mid;//如果小於我們所需要的就要往左邊找,因爲我們的a數組是排序過從大到小
else l = mid+1;//否則往右邊找,排序過的從大到小
}
for(int i = l;i <= cnt;i++)/*從l開始縮短搜索範圍,之間在這個還需要的長度的區間當中找合適的,
由於所有木棍是按從大到小的順序排的,因此從上述找到的木棍向右枚舉*/
{
if(vis[i] == false)
{
vis[i] = true;
dfs(k,i,res-a[i]);
vis[i]=false;
if(flag1 == true) return;//如果是我們找到目標就返回答案
if(res==a[i] || res==len) return;
/*當前正在拼的長棍剩餘的未拼長度等於當前小木棍的長度,
說明它只能自組,但繼續拼下去卻失敗,說明它不能自組。*/
/*當前木棍剩餘的未拼長度等於原始長度,說明這根原來的長棍還一點沒拼。
還需要繼續拼接,但繼續拼下去卻失敗 ,所以無法用上它。*/
//flag1=0 表示 繼續拼下去失敗
i = nxt[i];//把我們找到的這個i的值記錄到nxt數組的模塊當中
if(i == cnt) return;/*如果兩個是相同的話,
就是說我們已經到了這個循環的最後一個*/
}
}
return;
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i++)
{
scanf("%d",&t);
if(t <= 50)/*限制條件:每段的長不能超過這個50*/
{
a[++cnt] = t;
minx = max(minx,a[cnt]);//下界
maxn += a[cnt];//上界
}
}
sort(a+1,a+cnt+1,cmp);
nxt[cnt]=cnt;/*記錄當前有多少個相同的數的版塊只,能記錄就是nxt[cnt]=cnt
所以剛開始只有最後一個是成立的*/
for(int i=cnt-1;i>0;i--)//cnt是我們擁有的個數,所以我們的這個版塊的排序是從小到大,而不是從大到小
{
if(a[i] == a[i+1]) nxt[i]=nxt[i+1];/*如果前後兩個數是一樣的話,
那麼他們所代表的大小順序也是一樣的,就把他們分成同一版塊*/
else nxt[i]=i;//否則就各自一個佔用一個模塊
}
for(len = minx;len <= maxn/2;len++)/*長度最小就是我們定義的下線,
最大也不可能超過總和的一半,因爲至少要分成兩組*/
{
if(maxn % len == 0)//合法
{
m = maxn/len;
flag1 = 0;
vis[1] = 1;/*初始化全部都可以用*/
dfs(1,0,len-a[1]);/*從第一根木棍開始*/
vis[1] = 0;//恢復現場
if(flag1 == 1)//找到了目標(最上面的那一層遞歸返回來的)
{
printf("%d\n",len);//就輸出當前我們找到的這麼len的值
return 0;
}
}
}
printf("%d\n",maxn);/*如果找不到這個區間當中的len的話,
就直接輸出全部長度,也就是隻有一個木棍*/
return 0;
}