《挑戰程式設計競賽(第二版)》tmp
阿新 • • 發佈:2018-11-27
P162 超大揹包問題程式碼
#include <iostream>
#include <pair>
const int MAX_N =40;
typedef long long ll; int n; ll w[MAX_N], v[MAX_N]; ll W;
pair<ll, ll> ps[l<<(MAX_N/2)];
void solve() { //列舉前半部分 int n2=n/2;
for(int i=0; i<1<<n2; i++)
{
ll sw=0, sv=0;
for(int j=0; j<n2; ++j)
{
if(i>>j&1)
{
sw+=w[j];
sv+=v[j];
}
}
ps[i]=make_pair(sw, sv);
}
//去除多餘的元素 sort(ps, ps+(1<<n2)); int m=1; for(int i=1; i<1<<n2; ++i) { if(ps[m-1].second<ps[i].second)
ps[m++]=ps[i];
}
//列舉後半部分並求解 ll res=0; for (int i=0; i<1<<(n-n2); ++i) { ll sw=0, sv=0; for(int j=0;j<n-n2; j++) { if(i>>j &1) { sw+=w[n2+j]; sv+=v[n2+j]; } } if(sw<=W) { ll tv=(lower_bound(ps, ps+m, make_pair(W-sw, INF))-1)->second; res=max(res, sv+tv); } }
printf("%ld, ", res); }
P158, n個球體自由落體,彈性碰撞的問題。 #include <iostream> #include <cmath> using namespace std;
const double g =10.0;
int N=3; double H=100.0, R=10.0, T=4.9759;
double y[10];
double calc(double T) { if(T<0) return H; double t=sqrt(2*H/g); int k=(int)(T/t); if(k%2==0) { double d=T-k*t; return H-g*d*d/2; } else { double d=k*t+t-T; return H-g*d*d/2; } }
int main() { for(int i=0; i<N; i++) { y[i]=calc(T-i); } sort(y,y+N); for(int i=0;i<N;i++) { printf("%.2f%c", y[i]+2*R*i/100.0, i+1==N?'\n': ' '); } return 0; }
題目: 給出一字串,求包含此字串中任何一個字元的最短子串。
尺取法:
#include <iostream> #include <set> #include <map> using namespace std;
/* ex. * 1232221 * 1232224321 * 28022103228 * 11111 */ int P=11; int a[]={1,8,0,2,2,2,0,3,3,2,8}; //int P=10; //int a[]={1,2,3,2,2,2,4,3,2,1}; //int P=7; //int a[]={1,2,3,2,2,2,1}; //int P=5; //int a[]={1,1,1,1,1}; //int a[100];
void solve1() { set<int> all; for (int i=0;i<P;i++) { all.insert(a[i]); } int n=all.size();//原串各種不同字元的總數 int s=0, t=0, num=0; //[s,t]區間各種不同字元的總數 map<int, int> count;//記錄每個字元在[s,t]區間出現的次數 //int res=P; int rs=0, rt=P-1; //記錄最短子串的開始和結束位置 for(;;) { while(t<P && num<n) { if(count[a[t]]++==0) { num++; } if(num==n && ((rt-rs) > (t-s))) //記錄最新的更短串的起止位置 { rs=s; rt=t; } t++; }
if(s>=t) break; for(;s<P;) { if(--count[a[s++]]==0) { num--; } if(num<n) break; if(num==n && ((rt-rs) > (t-s)))//記錄最新的更短串的起止位置 { rs=s; rt=t; } if(s>t) goto end;
}
}
end: printf("rs=%d, rt=%d \n",rs, rt<P?rt:(P-1));//結果
}
int main() { solve1(); }
題目:給出一個表示式(只有+和*運算子),求任意加括號後的最大值。 例如: 0.6+3*0.7*4+2*2 加括號後 (0.6+3)*(0.7*4+2)*2 最大值為34.56
解法: #include <stdio.h> #include <stdlib.h>
typedef struct { float value;//該區間最大值 int divid;//此區間的劃分點 char set;//變更標識,0未計算;1已計算 } Texp;
//float num[]={0.2, 0.2, 0.5, 2.0, 6.0, 0.5}; //存放運算數 //表示式為 0.6+3*0.7*4+2*2 float num[]={0.6, 3, 0.7, 4, 2, 2}; Texp m[8][8] ; //表示運算數陣列的i到j區間的最大值 //char oper[]="+**+*"; //存放運算子號 char oper[]="+**+*";
int n; //表示式中運算數的個數
float getMin(float a, float b) { return a-b>0.000001?b:a; }
float getMax(float a, float b) { return a-b>0.000001?a:b; }
float getValue(float a, float b, int k) { if('+' == oper[k]) { return a+b; } else { return a*b; }
} //初始化m陣列 void init_Texp() { for(int i=0; i<n; ++i) for(int j=0; j<n; ++j) { m[i][j].set=0; m[i][j].value=0.0; m[i][j].divid=-1; } }
float set_m(int s, int e) { float v=0.0, v1=0.0; if(s>=e) { v=num[s]; m[s][e].divid=s; } else { for(int k=s;k<e;++k) { if(m[s][k].set==0) { m[s][k].value=set_m(s,k); m[s][k].set=1; } if(m[k+1][e].set==0) { m[k+1][e].value=set_m(k+1,e); m[k+1][e].set=1; } //在區間[s,e]中任意劃分中的最大運算值 v1=getValue(m[s][k].value, m[k+1][e].value, k); //v=getMax(v,v1); if(v1-v>0.000001) { m[s][e].divid=k; v=v1; } } } return v; } //列印各個數運算的先後順序 void trace_m(Texp *t, int s, int e, int level) { int k=(*t).divid; if(k-s>=1) { trace_m(&m[s][k], s, k, level+1); } if(e-k>=1) { trace_m(&m[k+1][e], k+1, e, level+1); }
if(e!=s) { printf("level<%d>: (%d,%d)\n", level, s, e); } }
int main() { n=sizeof(num)/sizeof(float); init_Texp(); float t=set_m(0, n-1); printf("max result=%f\n", t);//列印最大值 trace_m(&m[0][n-1], 0, n-1, 0);//列印解法
return 0;
}
題目: 設A和B是兩個字串,要用最少的字元操作,將字串A轉換為字串B,這裡的操作限定為: (1)刪除一個字元; (2)插入一個字元; (3)將一個字元變成另一個字元。 將字串A轉換成字串B所用的最少運算元稱為字串A到B的編輯距離,記為f(A, B),請設計演算法求f(A, B)。
解法: 設所給的兩字串分別為A=A[1..m]和B=B[1..n]; 狀態表示:考慮一般情形,即從字元子串A[1..i](按序)變換到字元子串B[1..j]的最少字元操作問題,設d(i, j)=f(A[1..i], B[1..j]),顯然,2個單字元a,b之間的編輯距離:當a!=b時,為f(a, b)=1;當a=b時,為f(a, b)=0,則原問題的解為f(m, n)。 最優子結構性質:設E=e[1] … e[k-1]e[k],k=d(i, j)為從字串A[1..i]按序變換得到字串B[1..j]的一個最少字串操作序列(即d(i, j)的一個最優解),則最後一個操作e[k]必屬於以下3種操作之一: (1)將字元A[i]改為字元B[j](如果A[i]=B[j],則e[k]為空操作,不參加計數),此時E1=e[1]…e[k-1]為d(i-1, j-1)的一個最優解; (2)刪除字元A[i],此時E1=e[1]…e[k-1]為d(i-1, j)的一個最優解; (3)插入字元B[j],此時E1=e[1]…e[k-1]為d(i, j-1)的一個最優解。 綜上可見,該問題具有最優子結構性質,可建遞迴關係如下: d(i, j)=min{d(i-1, j-1)+f(A[i], B[j]), d(i-1, j)+1, d(i, j-1)+1} 初始條件:d(i, 0)=i, i=0到m,d(0, j)=j, j=0到n。 問題的解為:d(m, n)
程式: #include <stdlib.h> #include <stdio.h> #include <string.h>
int f(char a, char b) { if(a!=b) return 1; else return 0; }
int min(int a, int b, int c) { int d; d=a<b?a:b; d=d<c?d:c; return d; }
//兩個字串的長度 #define M (16) #define N (15) //其他測試資料 //char A[]="lessp"; //char B[]="timedss"; ////char A[]="2020293"; //char B[]="21200191"; //char A[]="82025828"; //char B[]="83382068"; //char A[]="17220580"; //char B[]="1781118010"; char A[]="lesspagechecklog"; char B[]="timedoutwaiting";
//lena, lenb: 表示到A字串下標lena和B字串下標lenb為止的最小編輯距離 //d: 動態規劃用到的記錄陣列 int dp( int lena, int lenb, int d[][N+1]) { int i, j; for(i=0; i<=lena; ++i) d[i][0]=i; for(j=0; j<=lenb; ++j) d[0][j]=j; for(i=1; i<=lena; ++i) { for( j=1;j<=lenb;++j) { d[i][j]=min( d[i-1][j-1]+f(A[i-1], B[j-1]), d[i-1][j]+1, d[i][j-1]+1 ); } } return d[lena][lenb]; }
int main() { int d[M+1][N+1]; printf("A=%s\nB=%s\ndp=%d", A, B, dp(M,N, d)); return 0;
}
//------------------------------------------------------------ 例題: 給出一個數列(有正有負),求此數列中從某個位置開始連續若干個數和的最大值,簡稱最大子段和。 例如:{-2, 4, -3, 1, -1, 5}; 最大子段和是6; 子段是從{4,-3,1, -1, 5}
#include <stdlib.h> #include <stdio.h>
#define MIN_INF (-99999)
int max_sum=MIN_INF; int l=0, r=999;//表示解答結果的子段在原陣列中的左,右下標
//解法一: 這是遞迴解法,相比動態規劃真是遜色不少 void max_ziduanhe(int x, int y, int digit[], int len) { int sum_l=0, sum_r=0, sum=0 ; int i; printf("max_ziduanhe(%d, %d)\n",x, y); if(x>y) return ; //else if(x==y) // return digit[x]>max_sum ? digit[x]:max_sum; else { for (i=x; i<=y; ++i) sum+=digit[i]; if(max_sum< sum) max_sum=sum; //往右找到第一個正數 for(i=x+1; i<=y; ++i) if(digit[i]>0) break; int k=i; for(;i<=y;++i) sum_l+=digit[i]; if(max_sum<sum_l) { l=k; max_sum=sum_l; } max_ziduanhe(k, y, digit, len);
//往左找到第一個正數 for(i=y-1; i>=x; --i) if(digit[i]>0) break; k=i; for(; i>=x; --i) sum_r+=digit[i]; if(max_sum<sum_r) { r=k; max_sum=sum_r; } max_ziduanhe(x, k, digit, len); //return max_sum; } } //解法二: 這個方法是用動態規劃,程式和時空複雜度都少很多,一級棒。 /* 思路:
int main() { //用例 int digit[]={11, -4, 13, -5, -3, -2, 3}; //int digit[]={-2, 11, -4, 13, -5, 1, -3}; //int digit[]={-2, 11, -4, 13, -5, 6, -2}; //int digit[]={-2,4, -3,1,-1,5,-15, 12, -4, 13, -5,-3, -2, 3}; //int digit[]={-2, 4, -3, 1, -1, 5}; int len=sizeof(digit)/sizeof(int); printf("len=%d\n", len); l=0;r=len-1; //呼叫解法一求解 max_ziduanhe(l, r, digit, len); printf("sum=%d l=%d r=%d\n",max_sum, l, r); // 呼叫解法二求解 l=0;r=len-1; printf("use_DP()=%d, l=%d, r=%d\n", use_DP(digit, len), l, r);
return 0;
}
//-------------------------------------------------------------- #2.7.3節的Bride the prisoner題目的解法 #這是錯誤解法,因為這裡放入佇列時,區間和A元素沒有必然的對應關係。不過可以參考下deque用來做FIFO佇列的用法 #e.x. P=9, A=[1, 3, 4, 6, 7] def make_priovity_sequence(P, A): from collections import deque pq=[] #priovity queue s1=(1, P) qujian=deque() qujian.append(s1) sum=0 while A and qujian: s1=qujian.popleft() mid=(s1[0]+s1[1])/2 juli_min=P for m in A: juli=abs(m-mid) if juli<juli_min: m_min=m #must be assigned to m_min at least onece juli_min=juli pq.append(m_min) #the number to fetch out A.remove(m_min) sum=sum+s1[1]-s1[0] if m_min-1>=s1[0]: qujian.append((s1[0], m_min-1)) if m_min+1<=s1[1]: qujian.append((m_min+1, s1[1])) pq.append(sum) #返回的最後一個值不是釋放者編號,而是總金幣數 return pq
##2.7.3節的Bride the prisoner題目的解法 #這是正確解法,遞迴方式 #s0,s1分別表示區間的兩端,A為要求輸入的釋放者編號數列(假設已按從小到大排列),pq為最終輸出的釋放方法數列 #演算法關鍵思路是每次都找離中點最近的那個來釋放 def make_priovity_seq(s0, s1, A, pq): if A and s0<=s1: juli_min=s1-s0+1 mid=(s0+s1)/2 for m in A: juli=abs(m-mid) if juli<juli_min: m_min=m #must be assigned to m_min at least onece juli_min=juli pq.append(m_min) #the number to fetch out #A.remove(m_min) pos=A.index(m_min) A1=A[0:pos] A2=A[pos+1:] sum0=make_priovity_seq(s0, m_min-1, A1, pq) sum1=make_priovity_seq(m_min+1, s1, A2, pq) return s1-s0+sum0+sum1; #返回費用 else: return 0
#上面函式呼叫方法: if __name__ == '__main__': #s=make_priovity_sequence(20,[3,6,14]) aq=[] sum=make_priovity_seq(1, 20,[3,6,14], aq) print(sum)
typedef long long ll; int n; ll w[MAX_N], v[MAX_N]; ll W;
pair<ll, ll> ps[l<<(MAX_N/2)];
void solve() { //列舉前半部分
//去除多餘的元素 sort(ps, ps+(1<<n2)); int m=1; for(int i=1; i<1<<n2; ++i) {
//列舉後半部分並求解 ll res=0; for (int i=0; i<1<<(n-n2); ++i) { ll sw=0, sv=0; for(int j=0;j<n-n2; j++) { if(i>>j &1) { sw+=w[n2+j]; sv+=v[n2+j]; } } if(sw<=W) { ll tv=(lower_bound(ps, ps+m, make_pair(W-sw, INF))-1)->second; res=max(res, sv+tv); } }
printf("%ld, ", res); }
P158, n個球體自由落體,彈性碰撞的問題。 #include <iostream> #include <cmath> using namespace std;
const double g =10.0;
int N=3; double H=100.0, R=10.0, T=4.9759;
double y[10];
double calc(double T) { if(T<0) return H; double t=sqrt(2*H/g); int k=(int)(T/t); if(k%2==0) { double d=T-k*t; return H-g*d*d/2; } else { double d=k*t+t-T; return H-g*d*d/2; } }
int main() { for(int i=0; i<N; i++) { y[i]=calc(T-i); } sort(y,y+N); for(int i=0;i<N;i++) { printf("%.2f%c", y[i]+2*R*i/100.0, i+1==N?'\n': ' '); } return 0; }
題目: 給出一字串,求包含此字串中任何一個字元的最短子串。
尺取法:
#include <iostream> #include <set> #include <map> using namespace std;
/* ex. * 1232221 * 1232224321 * 28022103228 * 11111 */ int P=11; int a[]={1,8,0,2,2,2,0,3,3,2,8}; //int P=10; //int a[]={1,2,3,2,2,2,4,3,2,1}; //int P=7; //int a[]={1,2,3,2,2,2,1}; //int P=5; //int a[]={1,1,1,1,1}; //int a[100];
void solve1() { set<int> all; for (int i=0;i<P;i++) { all.insert(a[i]); } int n=all.size();//原串各種不同字元的總數 int s=0, t=0, num=0; //[s,t]區間各種不同字元的總數 map<int, int> count;//記錄每個字元在[s,t]區間出現的次數 //int res=P; int rs=0, rt=P-1; //記錄最短子串的開始和結束位置 for(;;) { while(t<P && num<n) { if(count[a[t]]++==0) { num++; } if(num==n && ((rt-rs) > (t-s))) //記錄最新的更短串的起止位置 { rs=s; rt=t; } t++; }
if(s>=t) break; for(;s<P;) { if(--count[a[s++]]==0) { num--; } if(num<n) break; if(num==n && ((rt-rs) > (t-s)))//記錄最新的更短串的起止位置 { rs=s; rt=t; } if(s>t) goto end;
}
}
end: printf("rs=%d, rt=%d \n",rs, rt<P?rt:(P-1));//結果
}
int main() { solve1(); }
題目:給出一個表示式(只有+和*運算子),求任意加括號後的最大值。 例如: 0.6+3*0.7*4+2*2 加括號後 (0.6+3)*(0.7*4+2)*2 最大值為34.56
解法: #include <stdio.h> #include <stdlib.h>
typedef struct { float value;//該區間最大值 int divid;//此區間的劃分點 char set;//變更標識,0未計算;1已計算 } Texp;
//float num[]={0.2, 0.2, 0.5, 2.0, 6.0, 0.5}; //存放運算數 //表示式為 0.6+3*0.7*4+2*2 float num[]={0.6, 3, 0.7, 4, 2, 2}; Texp m[8][8] ; //表示運算數陣列的i到j區間的最大值 //char oper[]="+**+*"; //存放運算子號 char oper[]="+**+*";
int n; //表示式中運算數的個數
float getMin(float a, float b) { return a-b>0.000001?b:a; }
float getMax(float a, float b) { return a-b>0.000001?a:b; }
float getValue(float a, float b, int k) { if('+' == oper[k]) { return a+b; } else { return a*b; }
} //初始化m陣列 void init_Texp() { for(int i=0; i<n; ++i) for(int j=0; j<n; ++j) { m[i][j].set=0; m[i][j].value=0.0; m[i][j].divid=-1; } }
float set_m(int s, int e) { float v=0.0, v1=0.0; if(s>=e) { v=num[s]; m[s][e].divid=s; } else { for(int k=s;k<e;++k) { if(m[s][k].set==0) { m[s][k].value=set_m(s,k); m[s][k].set=1; } if(m[k+1][e].set==0) { m[k+1][e].value=set_m(k+1,e); m[k+1][e].set=1; } //在區間[s,e]中任意劃分中的最大運算值 v1=getValue(m[s][k].value, m[k+1][e].value, k); //v=getMax(v,v1); if(v1-v>0.000001) { m[s][e].divid=k; v=v1; } } } return v; } //列印各個數運算的先後順序 void trace_m(Texp *t, int s, int e, int level) { int k=(*t).divid; if(k-s>=1) { trace_m(&m[s][k], s, k, level+1); } if(e-k>=1) { trace_m(&m[k+1][e], k+1, e, level+1); }
if(e!=s) { printf("level<%d>: (%d,%d)\n", level, s, e); } }
int main() { n=sizeof(num)/sizeof(float); init_Texp(); float t=set_m(0, n-1); printf("max result=%f\n", t);//列印最大值 trace_m(&m[0][n-1], 0, n-1, 0);//列印解法
return 0;
}
題目: 設A和B是兩個字串,要用最少的字元操作,將字串A轉換為字串B,這裡的操作限定為: (1)刪除一個字元; (2)插入一個字元; (3)將一個字元變成另一個字元。 將字串A轉換成字串B所用的最少運算元稱為字串A到B的編輯距離,記為f(A, B),請設計演算法求f(A, B)。
解法: 設所給的兩字串分別為A=A[1..m]和B=B[1..n]; 狀態表示:考慮一般情形,即從字元子串A[1..i](按序)變換到字元子串B[1..j]的最少字元操作問題,設d(i, j)=f(A[1..i], B[1..j]),顯然,2個單字元a,b之間的編輯距離:當a!=b時,為f(a, b)=1;當a=b時,為f(a, b)=0,則原問題的解為f(m, n)。 最優子結構性質:設E=e[1] … e[k-1]e[k],k=d(i, j)為從字串A[1..i]按序變換得到字串B[1..j]的一個最少字串操作序列(即d(i, j)的一個最優解),則最後一個操作e[k]必屬於以下3種操作之一: (1)將字元A[i]改為字元B[j](如果A[i]=B[j],則e[k]為空操作,不參加計數),此時E1=e[1]…e[k-1]為d(i-1, j-1)的一個最優解; (2)刪除字元A[i],此時E1=e[1]…e[k-1]為d(i-1, j)的一個最優解; (3)插入字元B[j],此時E1=e[1]…e[k-1]為d(i, j-1)的一個最優解。 綜上可見,該問題具有最優子結構性質,可建遞迴關係如下: d(i, j)=min{d(i-1, j-1)+f(A[i], B[j]), d(i-1, j)+1, d(i, j-1)+1} 初始條件:d(i, 0)=i, i=0到m,d(0, j)=j, j=0到n。 問題的解為:d(m, n)
程式: #include <stdlib.h> #include <stdio.h> #include <string.h>
int f(char a, char b) { if(a!=b) return 1; else return 0; }
int min(int a, int b, int c) { int d; d=a<b?a:b; d=d<c?d:c; return d; }
//兩個字串的長度 #define M (16) #define N (15) //其他測試資料 //char A[]="lessp"; //char B[]="timedss"; ////char A[]="2020293"; //char B[]="21200191"; //char A[]="82025828"; //char B[]="83382068"; //char A[]="17220580"; //char B[]="1781118010"; char A[]="lesspagechecklog"; char B[]="timedoutwaiting";
//lena, lenb: 表示到A字串下標lena和B字串下標lenb為止的最小編輯距離 //d: 動態規劃用到的記錄陣列 int dp( int lena, int lenb, int d[][N+1]) { int i, j; for(i=0; i<=lena; ++i) d[i][0]=i; for(j=0; j<=lenb; ++j) d[0][j]=j; for(i=1; i<=lena; ++i) { for( j=1;j<=lenb;++j) { d[i][j]=min( d[i-1][j-1]+f(A[i-1], B[j-1]), d[i-1][j]+1, d[i][j-1]+1 ); } } return d[lena][lenb]; }
int main() { int d[M+1][N+1]; printf("A=%s\nB=%s\ndp=%d", A, B, dp(M,N, d)); return 0;
}
//------------------------------------------------------------ 例題: 給出一個數列(有正有負),求此數列中從某個位置開始連續若干個數和的最大值,簡稱最大子段和。 例如:{-2, 4, -3, 1, -1, 5}; 最大子段和是6; 子段是從{4,-3,1, -1, 5}
#include <stdlib.h> #include <stdio.h>
#define MIN_INF (-99999)
int max_sum=MIN_INF; int l=0, r=999;//表示解答結果的子段在原陣列中的左,右下標
//解法一: 這是遞迴解法,相比動態規劃真是遜色不少 void max_ziduanhe(int x, int y, int digit[], int len) { int sum_l=0, sum_r=0, sum=0 ; int i; printf("max_ziduanhe(%d, %d)\n",x, y); if(x>y) return ; //else if(x==y) // return digit[x]>max_sum ? digit[x]:max_sum; else { for (i=x; i<=y; ++i) sum+=digit[i]; if(max_sum< sum) max_sum=sum; //往右找到第一個正數 for(i=x+1; i<=y; ++i) if(digit[i]>0) break; int k=i; for(;i<=y;++i) sum_l+=digit[i]; if(max_sum<sum_l) { l=k; max_sum=sum_l; } max_ziduanhe(k, y, digit, len);
//往左找到第一個正數 for(i=y-1; i>=x; --i) if(digit[i]>0) break; k=i; for(; i>=x; --i) sum_r+=digit[i]; if(max_sum<sum_r) { r=k; max_sum=sum_r; } max_ziduanhe(x, k, digit, len); //return max_sum; } } //解法二: 這個方法是用動態規劃,程式和時空複雜度都少很多,一級棒。 /* 思路:
定義b[j]:
含義:從元素i開始,到元素j為止的所有的元素構成的子段有多個,這些子段中的子段和最大的那個。
那麼:
如果:b[j-1] > 0, 那麼b[j] = b[j-1] + a[j]
如果:b[j-1] <= 0,那麼b[j] = a[j]
我們要求的最大子段和,就是是b[j]陣列中最大的那個元素。
*/
int use_DP (int digit[], int len) { int sum = 0 ; int temp = 0 ; int i; for ( i = 0; i < len; ++ i) { if (temp > 0) { temp += digit[i] ; } else { l=i; temp = digit[i] ; } if (temp > sum) { r=i; sum = temp ; } } return sum ; }int main() { //用例 int digit[]={11, -4, 13, -5, -3, -2, 3}; //int digit[]={-2, 11, -4, 13, -5, 1, -3}; //int digit[]={-2, 11, -4, 13, -5, 6, -2}; //int digit[]={-2,4, -3,1,-1,5,-15, 12, -4, 13, -5,-3, -2, 3}; //int digit[]={-2, 4, -3, 1, -1, 5}; int len=sizeof(digit)/sizeof(int); printf("len=%d\n", len); l=0;r=len-1; //呼叫解法一求解 max_ziduanhe(l, r, digit, len); printf("sum=%d l=%d r=%d\n",max_sum, l, r); // 呼叫解法二求解 l=0;r=len-1; printf("use_DP()=%d, l=%d, r=%d\n", use_DP(digit, len), l, r);
return 0;
}
//-------------------------------------------------------------- #2.7.3節的Bride the prisoner題目的解法 #這是錯誤解法,因為這裡放入佇列時,區間和A元素沒有必然的對應關係。不過可以參考下deque用來做FIFO佇列的用法 #e.x. P=9, A=[1, 3, 4, 6, 7] def make_priovity_sequence(P, A): from collections import deque pq=[] #priovity queue s1=(1, P) qujian=deque() qujian.append(s1) sum=0 while A and qujian: s1=qujian.popleft() mid=(s1[0]+s1[1])/2 juli_min=P for m in A: juli=abs(m-mid) if juli<juli_min: m_min=m #must be assigned to m_min at least onece juli_min=juli pq.append(m_min) #the number to fetch out A.remove(m_min) sum=sum+s1[1]-s1[0] if m_min-1>=s1[0]: qujian.append((s1[0], m_min-1)) if m_min+1<=s1[1]: qujian.append((m_min+1, s1[1])) pq.append(sum) #返回的最後一個值不是釋放者編號,而是總金幣數 return pq
##2.7.3節的Bride the prisoner題目的解法 #這是正確解法,遞迴方式 #s0,s1分別表示區間的兩端,A為要求輸入的釋放者編號數列(假設已按從小到大排列),pq為最終輸出的釋放方法數列 #演算法關鍵思路是每次都找離中點最近的那個來釋放 def make_priovity_seq(s0, s1, A, pq): if A and s0<=s1: juli_min=s1-s0+1 mid=(s0+s1)/2 for m in A: juli=abs(m-mid) if juli<juli_min: m_min=m #must be assigned to m_min at least onece juli_min=juli pq.append(m_min) #the number to fetch out #A.remove(m_min) pos=A.index(m_min) A1=A[0:pos] A2=A[pos+1:] sum0=make_priovity_seq(s0, m_min-1, A1, pq) sum1=make_priovity_seq(m_min+1, s1, A2, pq) return s1-s0+sum0+sum1; #返回費用 else: return 0
#上面函式呼叫方法: if __name__ == '__main__': #s=make_priovity_sequence(20,[3,6,14]) aq=[] sum=make_priovity_seq(1, 20,[3,6,14], aq) print(sum)