以求演算法
① 一段長40分鍾的視頻,如果以1.2倍速度播放,那實際用時是多少求演算法,謝謝。
40乘以60秒/分鍾等於2400秒,2400秒再除以1.2/1.0=2400/1.2=2000秒
② 求一個演算法(貪心演算法)
貪心演算法
一、演算法思想
貪心法的基本思路:
——從問題的某一個初始解出發逐步逼近給定的目標,以盡可能快的地求得更好的解。當達到某演算法中的某一步不能再繼續前進時,演算法停止。
該演算法存在問題:
1. 不能保證求得的最後解是最佳的;
2. 不能用來求最大或最小解問題;
3. 只能求滿足某些約束條件的可行解的范圍。
實現該演算法的過程:
從問題的某一初始解出發;
while 能朝給定總目標前進一步 do
求出可行解的一個解元素;
由所有解元素組合成問題的一個可行解;
二、例題分析
1、[背包問題]有一個背包,背包容量是M=150。有7個物品,物品可以分割成任意大小。
要求盡可能讓裝入背包中的物品總價值最大,但不能超過總容量。
物品 A B C D E F G
重量 35 30 60 50 40 10 25
價值 10 40 30 50 35 40 30
分析:
目標函數: ∑pi最大
約束條件是裝入的物品總重量不超過背包容量:∑wi<=M( M=150)
(1)根據貪心的策略,每次挑選價值最大的物品裝入背包,得到的結果是否最優?
(2)每次挑選所佔重量最小的物品裝入是否能得到最優解?
(3)每次選取單位重量價值最大的物品,成為解本題的策略。 ?
值得注意的是,貪心演算法並不是完全不可以使用,貪心策略一旦經過證明成立後,它就是一種高效的演算法。
貪心演算法還是很常見的演算法之一,這是由於它簡單易行,構造貪心策略不是很困難。
可惜的是,它需要證明後才能真正運用到題目的演算法中。
一般來說,貪心演算法的證明圍繞著:整個問題的最優解一定由在貪心策略中存在的子問題的最優解得來的。
對於例題中的3種貪心策略,都是無法成立(無法被證明)的,解釋如下:
(1)貪心策略:選取價值最大者。反例:
W=30
物品:A B C
重量:28 12 12
價值:30 20 20
根據策略,首先選取物品A,接下來就無法再選取了,可是,選取B、C則更好。
(2)貪心策略:選取重量最小。它的反例與第一種策略的反例差不多。
(3)貪心策略:選取單位重量價值最大的物品。反例:
W=30
物品:A B C
重量:28 20 10
價值:28 20 10
根據策略,三種物品單位重量價值一樣,程序無法依據現有策略作出判斷,如果選擇A,則答案錯誤。
所以需要說明的是,貪心演算法可以與隨機化演算法一起使用,具體的例子就不再多舉了。(因為這一類演算法普及性不高,而且技術含量是非常高的,需要通過一些反例確定隨機的對象是什麼,隨機程度如何,但也是不能保證完全正確,只能是極大的幾率正確)
================================
三個經典的貪心演算法
有人說貪心演算法是最簡單的演算法,原因很簡單:你我其實都很貪,根本不用學。有人說貪心演算法是最復雜的演算法,原因也很簡單:這世上貪的人太多了,那輪到你我的份?
不論難度如何,貪心演算法都是一個很重要的演算法,我在網上N多Online Judge中的題目中,總結了三類較為常見,也十分經典的貪心演算法,發布在這兒Just For Fun。
(註:由於沒有現成的名字可用,這三種類型貪心演算法的名字都是我自己取的,如果你聽著別扭,請見諒。)
No 1.線段覆蓋(linescover)
題目大意:
在一維空間中告訴你N條線段的起始坐標與終止坐標,要求求出這些線段一共覆蓋了多大的長度。
解題思路:
將線段按其坐標進行排序(排序的具體方法:按起始坐標排,起始坐標相同的按終止坐標排,都是小在前大在後),使之依次遞增,並按順序分別編號為X(i),X(i).a代表其起始坐標,X(i).b代表其終止坐標。
然後按排好的順序依次處理:定義一個變數last記錄考慮到當前線段之時被線段覆蓋的最大的坐標值,再定義一個變數length記錄當前線段覆蓋的長度。對於後面的線段,我們把它看成由兩個部分組成,即把它分成last之前的線段和last之後的線段。(如果線段全部處在last之後,其last之前的部分不存在。)由於我們排過序,我們可以肯定當前考慮的線段X(i)其處在last之前的部分不會對length造成影響(因為X(i-1).b=last,X(i).a>=X(i-1).a,即X(i)在last之前的部分所處位置肯定被線段X(i-1)覆蓋過),所以會對length產生影響的即是X(i)處在last之後的部分。
所以我們可以依次對每條線段做如下處理:(初始化length為零,last為負無窮)
length+=X(i).b-last (X(i).a<=last 且 X(i).b>=last)
length+=X(i).b-X(i).a (X(i).a>last)
last=X(i).b;
最後length就為我們所需要的答案。
No 2.最優數對(bestpair)
題目大意:
按遞增的順序告訴你N個正整數和一個實數P,要求求出求出該數列中的比例最接近P的兩個數(保證絕對沒有兩個數使得其比值為P)。
解題思路:
定義兩個指針i和j,先初始化i=j=1,然後進行如下操作:
當code[j]/code[i]>p時,inc(j);
當code[j]/code[i]<p時,inc(i)。
記錄其中產生的最優值即為答案。
No 3.連續數之和最大值(maxsum)
題目大意:
給出一個長度為N的數列(數列中至少有一個正數),要求求出其中的連續數之和的最大值。(也可以加入a和b來限制連續數的長度不小於a且不大於b)。
解題思路:
先說不加限制的那種,定義一個統計變數tot,然後用循環進行如下操作:inc(tot,item) 其中如果出現tot<0的情況,則將tot賦值為0。在循環過程之中tot出現的最大值即為答案。
如果加入了限制條件的話,問題就變得難一些了(這句真的不是廢話)。為此我們先定義數組sum[i]來表示code[1]到code[i]之和(這樣的話code[a]~code[b]的和我們就可以用sum[b]-sum[a-1]來表示了。)。
再維護一個數組hash[i]來表示滿足條件的sum[a-1]的下標,並使之按遞增順序排列,這樣當前以第i的數為終止的數列的最大值肯定就是sum[i]-sum[hash[1]]。
現在我們來討論hash數組之中的數據需要滿足的條件和如何維護的具體問題:
當考慮到以第i個數為結尾時,hash[i]所表示的下標需要滿足的第一個條件就是題目規定的長度限制,我們需要實時的加入滿足長度規定的下標,刪除不符合要求的下標。其次,與不加限制條件時相同,若sum[i]-sum[hash[1]]的值小於零,則清空數組hash。
維護時可以這樣,當考慮到第i個數時,我們就將下標i-a+1加入到hash中,因為hash中原來已經排好序,因此我們我們可以用插入排序來維護hash的遞增性,然後我們考察hash[1],若hash[1]<i-b+1,則證明其已超出長度限制,我們就將其刪除,接著再考慮更新後的hash[1],如此重復直至找到一個滿足條件的hash[1]為止。
我們可以用鏈表來表示hash,這樣就可以減少數據加入和刪除時頻繁數據移動的時間消耗。
記錄下sum[i]-sum[hash[1]]的最大值即為答案。
③ 設計演算法以求解從集合{1..n}中選取k(k<=n)個元素的所有組合
C(k,n-1)=∏(n-k,n-1)/k!
C(k-1,n-1)=∏(n-k+1,n-1)/(k-1)!
C(k-1,n)+C(k-1,n-1)
=∏(n-k,n-1)/k!+∏(n-k+1,n-1)/(k-1)!
=∏(n-k,n-1)/k!+k·∏(n-k+1,n-1)/k!
=[(n-k)·∏(n-k+1,n-1)!+k·∏(n-k+1,n-1)]/(k-1)!
=[n·∏(n-k+1,n-1)]/k!
=∏(n-k+1,n)]/k!
=C(k,n)
即:C(k-1,n)+C(k-1,n-1)=C(k,n)
說簡單點,就是楊輝三角形的元素演算法。
此原理應用到你的問題上,重點是:結果集合的每個元素又是個集合。
若通用集合類Set(其實java中Set就是);
new Set{value...}為構造方法,-{value..}為集合差,+{value...}為集合和,Set(i)和集合第i個元素;
對於n個元素的集合Sn,如果有函數Set combine(k,Sn),產生n個元素中選k個元素集合的集合;那麼,當a是n個元素中的任意一個時,combine(k,Sn)=combine(k,Sn-{a})+combine(k-1,Sn-{a})。
由此可以產生遞歸演算法:
Set Sn=new Set{a0,a1,...an-1};
Set result=Sn.combine(k,Sn);
...........................
...........................
function Set combine(int count,Set S){
if(count==S.size()){
return new Set{S};//這是集合S僅為結果集的一個元素
}
if(count+1==S.size()){
Set result=new Set{};
for(Element a:S){
result+=new Set{S-{a}};//集合依次排除一個元素產生的子集作為結果的一個元素
}
return result;
}
Set S2=combine(count-1,S-S(0));//對應YH公式的後一項,S(0)為集合S的第一個元素
for(Set Si:S2){
Si+={S(0)};//Si是缺了一個元素的
}
Set S1=combine(count,S-S(0));//這個是個數整好的,YH公式的前一項
return S1+S2;//YH公式
}
這個問題比較有意思,不知道誰出的。沒有中學組合知識或YH公式,真困難了。
要是誰有更好演算法,不妨交流一下。
這題分給的夠低了,純屬興趣做一下玩。
④ 旋轉角度坐標移動 求演算法
旋轉基點是哪裡?逆時針轉是順時針轉?
假設基點為O(a,b)
A(m,n),以O為基點旋轉角度α(以逆時針轉為正,順時針轉為負)!
旋轉後的點為B(x,y)
那麼△OAB為等腰三角形,OA=OB,∠AOB=α
以基點為原點建立二維坐標系(如圖)
其中OA與x軸的夾角設為β(0~2π),tanβ=(n-b)/(m-a),可求出β.
而OB與x軸的夾角設為(α+β)(0~2π),tan(α+β)=(y-b)/(x-a);
又tan(α+β)=(tanα+tanβ)/(1-tanα·tanβ)
可求出tanα為一個用a、b、m、n和x、y表示的式子------(1)
OA=OB,可求出:(x-a)^2+(y-b)^2=(m-a)^2+(n-b)^2------(2)
再由正弦定理:OA/sin(90-α/2)=AB/sinα------(3)
由以上3式可求出xy(關於abmn表示的式子)
可求出OB的直線方程,再求出沿OB向前100的坐標!
⑤ 若已知角度為20°的扇形,則變為弧度是()rad。 求演算法 謝謝!
π/9
首先,一個圓,360°=2π
則該題弧度演算法:20°÷360°×2π=π/9
⑥ 求演算法:假設以塊鏈結構表示串
#include <stdio.h>
#include <string.h>
#include <malloc.h>
typedef struct _ChNode
{
int ch;
struct _ChNode *next;
} ChNode;
ChNode* cncreate(const char *str)
{
ChNode *head = NULL, *tail, *node;
while (*str)
{
node = (ChNode *)malloc(sizeof(ChNode));
node->ch = *str;
node->next = NULL;
if (head == NULL)
{
head = node;
tail = node;
}
tail->next = node;
tail = node;
str++;
}
return head;
}
ChNode* cnchr(ChNode *node, int ch)
{
while (node)
{
if (node->ch == ch)
{
return node;
}
node = node->next;
}
return NULL;
}
ChNode* cntail(ChNode *node)
{
while (node->next)
{
node = node->next;
}
return node;
}
void cnins(ChNode *a, ChNode *b)
{
ChNode *btail = cntail(b);
btail->next = a->next;
a->next = b;
}
void cncat(ChNode *a, ChNode *b, int ch)
{
ChNode *node = cnchr(a, ch);
if (node == NULL)
{
node = cntail(a);
}
cnins(node, b);
}
void cnprint(ChNode *node)
{
while (node)
{
printf("%c", node->ch);
node = node->next;
}
printf("\n");
}
void main()
{
ChNode *list1, *list2;
list1 = cncreate("show me the money!");
cnprint(list1);
list2 = cncreate("give me five!");
cnprint(list2);
cncat(list1, list2, 'w');
cnprint(list1);
}
⑦ 給出一組數字,求公式演算法
從A的x到B的x,方程是y=1.3625x-127.8
我用matlab描點畫圖得到一條幾乎筆直的線
然後進行1次擬合,代碼如下
x=[400,440,480,520,560];
y=[416,473,526,582,634];
plot(x,y,'o')
a=polyfit(x,y,1)
a
擬合結果:
a = 1.3625 -127.8000
所以就是這樣的。既然樓主說近似,絕對沒錯咯!呵呵
⑧ 高分求演算法:尋找與特定對象距離最近的對象
用現在的ID,X,Y三個元素來找最近點的話無論什麼辦法,都至少要與每個點進行一次距離的判斷,比較X或者Y也好,計算距離(當然這個距離是不用開平方的,與其他所有點的距離都不開平方也能比較相對的距離長短)也好,所以如果只有這三個元素的話,其實再怎麼改也不會有太大的優化的。
最好還是添加一些輔助信息,比較常用的就是以劃分網格的方法,將所有點分派在不同的網格中,然後以網格為基礎找最近點,這樣的話就要加一個網格的結構(以空間換時間),裡面存放的就是屬於這個網格的點的ID,通過編號規則可以很快的找最近的網格,然後再找裡面的點,這樣可以提高點查找速度。
呵呵,不好意思,沒想清楚就寫了:P,改一下,改一下,再加一步就好了
1.給點添加一個所屬網格的信息:
Class A
{
public int ID;
public int X;
public int Y;
publci int Index;//所屬網格編號
}
2.構造一個點鏈表,這是為了減少空間和方便處理而加的,後面的演算法里會感覺到用處
(為每個點對象建立一個點鏈表節點)
Class I
{
public A *pAObject; //指向一個點對象
publci I *pNextA; //指向下一個
}
3.構件一個網格信息結構
Class G
{
public int ID; //網格編號
public I *pAObjects; //指向一個點鏈表(至於這個是帶頭節點還是不帶頭節點的都一樣啦)
//這里用鏈表比較好
//第一,因為點可移動,那麼點對象個數可以隨意,可以發揮鏈表的擴展性
//第二,不需要取中間節點,也就沒有用到數組的長處
}
4.構建網格,比如以1000為長度(長度可以自己定義,關鍵是平衡空間和速度關系),建立正方形的網格,那麼(0,0)到(1000000,1000000)就是有1000*1000個網格(在給網格編號的時候最好有規律,那麼就能通過List或者其他數組容器的下標來獲得網格信息)
5.添加點的時候,先判斷屬於哪個網格,然後在點信息中添加網格編號,同時構建對應的點鏈表節點,並添加到所屬網格的鏈表中
6.最後就是查詢點了,首先獲得出發點中的所屬網格信息,看這個網格中是否有其他點:有,則一個個的判斷距離(距離的平方),那麼最近點一定在這些點裡面(如果點還是太多,那麼就考慮縮小網格的范圍);沒有,那就找所屬網格周邊8個網格中是否有點,有,則再找最近點,沒有就再往外擴(如果感覺網格太多,可以加大網格的范圍)
7.以找到的最近點到出發點的距離為基準,看看出發點到周邊網格在這個距離內會接觸到的(沒有在6中遍歷過的)有幾個網格,把這些網格中的點再查看有沒有更近的就行了
注:
1.如果還要優化就是加大網格的層次,可以用多層網格,這就會更復雜一點
2.在網格信息結構(G)中,因為點會移動,那麼刪點鏈表節點會有一個查找過程,如果覺得這個慢,那麼就在點對象結構體中加一個所屬點鏈表的指針:
class I;
Class A
{
public int ID;
public int X;
public int Y;
publci int Index;//所屬網格編號
publci I *pI;
}
....
呵呵,看了lipai006的回答,想了下似乎也是可以實現的,只要多花點內存就可以達到比較好的速度了,而且也不需要真的從X坐標出發這樣慢慢的以扇形擴展了啦,通過做一些輔助結構,就直接可以從出發點的X坐標出發,找同X不同Y中Y坐標與出發點最近的就行啦,循環結束條件就是X的擴展距離已經大於當前最小距離,因為再往外也肯定比當前最小距離大了。這個方法也就是要更復雜一些的輔助結構做索引,添加點的時候也要多做些事情,而且實現上的代碼相對網格方法復雜一些,但查找速度應該比網格會快一點,因為畢竟是直接找點去了,其實網格方法就是把一批點的X,Y坐標看成是一樣的,這樣先過濾一批而已,是個速度與復雜度的折中。
xx_lzj:劃分區域的目的就是為了使每個區域內的點不能太多,根據我的結構,每個區域有沒有點,一個bool判斷就夠了,不會存在太稀疏影響效率的事情,不過最壞的情況的確會退化到遍歷整個點空間,所以這個方法的時間復雜度仍然是O(n)。
你的方法其實和lipai006說的原理是差不多的(如果我對你們鏈表結構的猜想准確的話),無非就是通過X,Y坐標形成一個二維雙向鏈表,在形成這個鏈表的過程會比網格相對復雜一點,而且也不是像你想的只要判斷8個點就夠的,當只有一個點在中間,其他點分布成以這個點為圓心的圓周上時,按照貼主的要求,難道只有8個最近點嗎??在這個情況下,你的最壞復雜度還是O(n),但就如我說過的,這個方法的平均時間復雜度在參數上是會比網格的低一點,但是演算法本身的代碼復雜度上會高一點,而且在插入點的過程中的時間消耗會大一點而已。我覺得這是一個整體的過程,不能為了查找的快速犧牲太多其他的時間。
*************
xx_lzj:不好意思,你的鏈表我還有些不明白的地方:1.二維雙向鏈表每個節點有4個指針,你能否把這4個指針如何獲得的說一下,最好不要取邊界線上的點,取中間的一個點進行介紹。2.對於初始化和修改點坐標的時候,現有數據如果是鏈表結構(不是數組),如何能不依靠其他輔助數據進行折半查找?3.修改某個點坐標之後,根據你的鏈表結構,我感覺不是刪除、插入節點這么簡單的,能不能具體點說明。