sdnu山東省ACM 2010年第一屆省賽Greatest Number
阿新 • • 發佈:2019-02-15
原題連結:http://210.44.14.31/problem/show/1141
注意事項:
範圍太大,爆搜超時。
本人猜測,本題的測試資料可能均是需要四個數才能的出最優解(依據第二個程式碼)。
具體分析看程式碼。
程式碼如下:
#include<iostream> #include<algorithm> #include<cstdio> //別用cin,本題測試資料較多 using namespace std; int num[1000000 + 10]; int main() { int n, m; int k = 0; while (scanf_s("%d%d",&n,&m)&&(n||m)) { num[0] = 0; int max = 0; for (int i = 1; i <= n; i++) { scanf_s("%d", &num[i]); if (num[i]>m) //超範圍的直接去除 { n--; i--; continue; } } //此時num數組裡有 1到n 個單個的值 int cnt = n+1; cout << "Case " << ++k << ": "; for (int i = 1; i <= n; i++) for (int j = i; j <= n; j++) if (num[i] + num[j]<=m) num[cnt++] = num[i] + num[j]; //此時前n個位單個值,n+1到cnt為不大於m的所有兩個值的和 sort(num, num + cnt); int left, right=cnt-1,nextright=cnt-1; for (int i = 0; i < cnt; i++) //因為數組裡有單個值也有兩個值的和 所以會出現 兩個值的和、三個值的和、四個值的和 { left = i; while (left <= right) { int mid = (left + right) >> 1; int tempmax = num[i] + num[mid]; if ( tempmax< m) { left = mid + 1; if ( tempmax> max) { max = tempmax; nextright = mid; //關鍵:在已經排好序的數組裡,從i到i+1 i對應的mid必定在i+1對應的mid 的右邊即mid(i)>=mid(i+1)這樣做可以提高效率 } } else if (tempmax>m) right = mid - 1; else //找到直接結束迴圈 { max = m; i = cnt; break; } } right = nextright; //將right減小 } cout << max << endl<<endl; } return 0; }
附另解(本解法只用了四個數的組合便可ac,程式碼簡單就不做詳解了):
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int str[1000010],op[1010]; int main() { int i,j,k,n,m,ans,t=1; bool flag=false; while(~scanf("%d%d",&n,&m),n||m) { op[0] = k = 0; for(i=1;i<=n;i++) scanf("%d",&op[i]); sort(op,op+n+1); for(i=0;i<=n;i++) for(j=i;j<=n;j++) { if(op[i]+op[j]<=m) str[k++] = op[i]+op[j]; } sort(str,str+k); ans = 0; for(i=0;i<k;i++) { int low=i,high=k-1; while(low<high) { int mid=(low+high)/2; if(str[i]+str[mid]>m) high = mid-1; else low = mid+1; } if(str[i]+str[low]>ans && str[i]+str[low]<=m) ans = str[i]+str[low]; } if(flag) printf("\n"); printf("Case %d: %d\n",t++,ans); flag = true; } return 0; }