[NHZXOI2017]2016NOIP普及组复赛题解

  用了两天的中午时间做了一套今年的普及组复赛试题结果测出来分数只有两百!(第三题洛谷全过,lemon却显示编译错误,奇了怪了)

第一题  买铅笔

考查知识点:数学

解题思路:把每个产品买够n支笔时的钱算出来,选取最小的。

代码:

#include 
#include 
using namespace std;
char ch;
int getnum()
{
	ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar());
	int ret = 0;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) ret = ret * 10 + ch - 48;
	return ret;
}
int i,k,a,b,minnum;
int main()
{
	k = getnum(); 
	minnum = 1 << 30;
	for (i = 1; i <= 3; i++)
	{
		a = getnum(); b = getnum();
		if (a < k) 
		  if (k % a != 0) b = b * (k / a + 1); else b = b * (k / a);
		if (b < minnum) minnum = b;
	}
	printf("%d",minnum);
	return 0;
}

第二题:回文日期

考察知识点:日期模拟,判断回文数

题解

①:设置三个变量,分别模拟年、月、日。

    注意当月有多少天?当年有多少天?

②:判断回文数,把原来的数翻转然后跟原来的数判断是否相同即可

代码:

#include 
#include 
using namespace std;
char ch;
int date[4],arrived_date,num,ans;
int getnum()
{
	ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar());
	int ret = 0;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) ret = ret * 10 + ch - 48;
	return ret;
}
void iread()
{
	int temp,i;
	temp = getnum();
	date[1] = temp / 10000; temp %= 10000;
	date[2] = temp / 100; temp %= 100;
	date[3] = temp;
	arrived_date = getnum();
}
bool check()
{
	int i,x = 0,y = 0,temp;
	x = date[1] * 10000 + date[2] * 100 + date[3];
	temp = x;
	for (i = 8; i >= 1; i--)
	{
		y = y * 10 + temp % 10; temp /= 10;
	}  
	if (x == y) return true; else return false;
}
bool check_arrived()
{
	int i,x = 0,y = 0;
	x = date[1] * 10000 + date[2] * 100 + date[3];
	if (x <= arrived_date) return false; else return true;
}
bool check_year(int year)
{
	if (year % 100 != 0 && year % 4 == 0) return true;
	if (year % 400 == 0) return true;
	return false;
}
void solve()
{
	while (!check_arrived())
	{
		if (check()) ans++;
		date[3]++;
		if (date[3] > 31 && (date[2] == 1 || date[2] == 3 || date[2] == 5 || date[2] == 7 || date[2] == 8 || date[2] == 10 || date[2] == 12))
		{
			date[2]++; date[3] = 1;
		}
		if (date[3] > 30 && (date[2] == 4 || date[2] == 6 || date[2] == 9 || date[2] == 11))
		{
			date[2]++; date[3] = 1;
		}
		if (date[2] == 2 && ((date[3] > 28 && !check_year(date[1])) || (date[3] > 29 && check_year(date[1]))))
		{
			date[2]++; date[3] = 1;
		}
		if (date[2] > 12){
			date[1]++; date[2] = 1;
		}
	}
}
int main()
{
	
	iread();
	ans = 0;
	solve();
	cout << ans;
	return 0;
}

第三题:海港

考查知识点:模拟、指针、对时间、空间复杂度的计算

题解

  先来说一下怎么做吧。一个是判断日期相距是否在一天内,另一个是判断有多少个乘客。

判断时差:我们可以把船按时间先后顺序排列好(题目给出就已经是这样的)假如现在的船到达的时间是time[i],那么可以选择从第一个和i时差一天的船的位置。但由于时间是单调不减的,因此,在算与i时差一天的船位置时,可以在前一艘船的基础上再往后找,就可以避免重复比较次数。

判断当前有多少乘客:乘客是按数字来区分国家的,因此可以用一列数组记录当前i国家的人数有多少个。每加入一艘船的时候,就逐个记录好(+1)。相对的,如果这艘船计算的时候不符合时差一天的条件,就逐个减去——同时不要忘记了若减了后出现“0”,就代表少了一个国家的游客。

注意:

 ,, 

设置数组的时候如果单纯的设置二维100000 * 3 * 100000,就中了出题者的陷阱。——空间溢出!所以建议在保存游客信息的时候用指针记录,能节省大量空间。而c++党就可以用STL中的vector(懒人必备!)

代码:

#include 
#include 
#include 
using namespace std;

char ch;
const int MAX_N = 100000 + 4;
const int MAX_X = 3 * 100000 + 4;
int n,l,visitor,i,j,k,time[MAX_N],num[MAX_X];
vector  x[MAX_N];

int getnum()
{
	ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar());
	int ret = 0;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) ret = ret * 10 + ch - 48;
	return ret;
}

int main()
{
	int temp;
	n = getnum(); l = 1; visitor = 0;
	for (i = 1; i <= n; i++){
		time[i] = getnum(); k = getnum();
		for (j = 1; j <= k; j++) 
		{
			temp = getnum();
			x[i].push_back(temp);
			num[temp]++; 
			if (num[temp] == 1) visitor++;
		}
		while (time[i] - time[l] >= 86400)
		{
			for (j = 0; j < x[l].size(); j++){
				num[x[l][j]]--;
				if (num[x[l][j]] == 0) visitor--;
			} 
			l++;
		}
		printf("%d\n",visitor);
	}
	return 0;
}


第四题:魔法阵

考查知识点:枚举、前缀和、数学分析或者枚举+剪枝

题解

80分做法:枚举+剪枝

    按照题目意思,枚举每个物品当A、B、C、D的情况。这样看起来是很简单,但是这样的枚举可能有两三十分的差距。目测80为枚举最高分。

      所以就要看大家剪枝的功夫了。分析式子,用a代表A位置的物品魔法值,发现a——①  b-a=2(d-c)——②  4 * b-3 * a——③

          分析①和③,发现可以在枚举的时候做限制条件。②就提示我们,A、B、C都确定的时候,D就可以直接算出来

          还有物品的魔法值可能相同,所以可以根据乘法原理算出某个魔法值充当A或B或C或D的数值,最后再分别输出物品相应的魔法值的充当A、B、C、D的次数。——完美!80分。


100分做法:枚举、前缀和、数学分析

这题的正解的确很666。打完模拟后,你就开始寻思哪里有你忽略的地方或者重复计算的地方。

,,

回想一下刚才模拟的方法里面,先算出某个魔法值其充当A、B、C、D的次数,而物品的数据只是用来计算的时候起到一点作用。就可以想到是不是可以直接在魔法值上面做。枚举出A、B、C、D来。首先,由模拟思路得出,A,B,C得出后D就确定,时间为O(n * n * n)不过,我们重新分析一下式子②d和c的距离(魔法值差值)为b和a的一半,③的原式——xb-xa<(xc-xb)/3,有c和b的距离和b和c的距离关系。

设:d - c = i

则 a - b = 2 * i         c - b > 6 * i

因此,我们可以枚举这个距离差,然后分别枚举a和d的距离。由于距离关系,我们省去了一点时间。敏感的人会发现,这其中有重复的计算。即a确定,i(距离)确定,枚举c到b的距离即可,但是当前a的计算和前一个a的计算有重叠!!!所以应该调整一下计算顺序,用前缀和或者(后缀和)记录一下就好了。


代码:

模拟80

#include 
#include 
#include 
using namespace std;

char ch;
const int MAX_N = 40000 + 4;
int a[MAX_N],b[MAX_N],c[MAX_N],num[MAX_N][5],aa,bb,cc,dd,map[5],n,m,tip[MAX_N];
int getnum()
{
    ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar());
    int ret = 0;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) ret = ret * 10 + ch - 48;
    return ret;
}
bool cmp(int a,int b)
{
    return a < b;
}
int qfind(int num, int l, int r)
{
    int mid;
    while (l < r)
    {
        mid = (l + r) / 2;
        if (num <= b[mid]) r = mid - 1; else l = mid;
    }
    return l;
}
void dfs(int k)
{
    if (k == 4)
    {
        aa = map[1]; bb = map[2]; cc = map[3];
        if ((b[bb] + 2 * b[cc] - b[aa]) % 2 !=0) return;
        dd=(b[bb] + 2 * b[cc] - b[aa]) / 2;
        if (tip[dd] && dd > b[cc]){
            num[b[aa]][1] += tip[b[bb]] * tip[b[cc]] * tip[dd]; 
            num[b[bb]][2] += tip[b[aa]] * tip[b[cc]] * tip[dd];
            num[b[cc]][3] += tip[b[aa]] * tip[b[bb]] * tip[dd];
            num[dd][4] += tip[b[aa]] * tip[b[bb]] * tip[b[cc]];
            //cout << b[aa] << ' ' << b[bb] << ' ' << b[cc] << ' ' << dd << endl;
        } 
        return;
    }
    int i;
    for (i = map[k - 1] + 1; i <= n; i++)
    {
        if (k == 3 && b[i] <= b[map[2]] * 4 - b[map[1]] * 3) continue;
        map[k] = i;
        dfs(k + 1);
    }
}

int main()
{
    
    int i,j;
    n = getnum(); m = getnum();
    for (i = 1; i <= m; i++) c[i] = a[i] = getnum();
    sort(a+1,a+m+1,cmp);
    j = 1;
    for (i = 1; i <= m; i++)                        //去重 
    {
        tip[a[i]]++;
        if (a[i] == b[j - 1]) continue;
        b[j] = a[i]; j++;
    }
    n = j - 1;
    dfs(1);
    for (i = 1; i <= m; i++)
        cout << num[c[i]][1] << ' ' << num[c[i]][2] << ' ' << num[c[i]][3] << ' ' << num[c[i]][4] << endl;
    return 0;
}
100分:

#include 
#include 
#include 
#include 
using namespace std;

char ch;
const int MAX_N = 40000 + 4;
int a[MAX_N],b[MAX_N],c[MAX_N],d[MAX_N],sum,n,m,tip[MAX_N],v[MAX_N];
int getnum()
{
    ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar());
    int ret = 0;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) ret = ret * 10 + ch - 48;
    return ret;
}
int main()
{
    
    int i,j;
    n = getnum(); m = getnum();
    for (i = 1; i <= m; i++) 
    {
        v[i] = getnum(); tip[v[i]]++;
    }
    for (i = 1; i * 9 + 1 <= n; i++)
    {
        sum = 0;
        for (j = n - 9 * i  - 1; j >= 1; j--)
        {
            sum += tip[j + 8 * i + 1] * tip[j + 9 * i + 1];
            a[j] += tip[j + 2 * i] * sum;
            b[j + 2 * i] += tip[j] * sum;
        }
        sum = 0;
        for (j = 1; j <= n - 9 * i  - 1; j++)
        {
            sum += tip[j] * tip[j + 2 * i];
            c[j + 8 * i + 1] += tip[j + 9 * i + 1] * sum;
            d[j + 9 * i + 1] += tip[j + 8 * i + 1] * sum;
        }
    }
    for (i = 1; i <= m; i++)
        cout << a[v[i]] << ' ' << b[v[i]] << ' ' << c[v[i]] << ' ' << d[v[i]] << endl;
    return 0;
}



你可能感兴趣的:(【NOIP2016】)