1. 程式人生 > >sdnu山東省ACM 2010年第一屆省賽Greatest Number

sdnu山東省ACM 2010年第一屆省賽Greatest Number

原題連結: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;
}