upc 个人训练赛第一场:叠罗汉+踢石头(贪心+优先队列)

问题 A: 打印方阵

题目描述
下面这样的方阵很有规律,称为蛇形方阵。例如33的:
1 2 3
6 5 4
7 8 9
现在给定边长,输出相应的蛇形方阵。
输入
1个整数n,表示要输出n
n的蛇形方阵,1<=n <=100。
输出
n行,每行n个整数,空格隔开。
样例输入 Copy
4
样例输出 Copy
1 2 3 4
8 7 6 5
9 10 11 12
16 15 14 13

思路:
第一行从左到右增大,下一行从右向左增大,只需要定义一个变量k,k = 0时,从左向右增大,一行数填完之后标记k = 1,继续循环即可

int a[105][105];
int n,k,tot;
int main()
{
    cin >> n;
    memset(a,0,sizeof(a));
    k = 0,tot = 1;
    for(int i=0;i<n;i++)
    {
        if(k == 0)
        {
            for(int j=0;j<n;j++)
            {
                a[i][j] = tot++;
                k = 1;
            }
        }
        else
        {
            for(int j=n-1;j>=0;j--)
            {
                a[i][j] = tot++;
                k = 0;
            }
        }
    }
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            printf("%d ",a[i][j]);
        }
        printf("\n");
    }
    return 0;
}

问题 B: 分数减法

题目描述
这样的分式运算我们都会。请编程计算两个分数相减的结果。

输入
第一行2个整数a和b,表示一个分数a/b,1<=a 第二行2个整数c和d,表示一个分数c/d,1<=c 输入数据保证计算结果为正。

输出
2个整数,表示结果。
提示:运算结果分式要约分。

样例输入 Copy
4 7
1 3
样例输出 Copy
5 21

思路:
先计算b、d的最小公倍数,把分母通分,然后计算新的分子分母的最大公约数,对结果进行约分。要考虑如果最后分子是分母倍数的情况

ll a,b,c,d,x,y,z;
int main ()
{
    cin >> a >> b;
    cin >> c >> d;
    z = gcd(b,d);//最大公约数 
    y = b*d/z;//最小公倍数 
    x = a*(y/b)-c*(y/d);
    if(x/y == 1)	printf("1\n");
    else
    {
        if(x%y == 0)	printf("%d\n",x/y);
        else
        {
            z = gcd(x,y);
            x = x/z;
            y = y/z;
            printf("%d %d\n",x,y);
        }
    }
    return 0;
}

问题 E: 可表示的数

题目描述
有N个整数从左到右排成一行,如果某个数等于它前面的2个数的和,就称这个数是可以表示的数。问给定的数列里有多少个数是可以表示的数。
输入
第一行1个整数N,表示数列有多少个整数。1<=N<=10000。
第二行N个正整数,每个正整数不超过10000。

输出
一个整数,有多少可表示的数。
样例输入 Copy
8
5 2 2 3 4 8 7 16
样例输出 Copy
3
提示
4=2+2;8=5+3; 7=3+4

思路:
三层循环会超时,所以考虑用两层循环解决。把两个数相加之后的和用数组标记为1,当标记数组为1时,则结果++。这里要注意每次在最前面进行if的判断,这样可以保证是这个数的前面两个数的和

int n,ans;
int a[10005],vis[20005];
int main()
{
	cin >> n;
	for(int i=0;i<n;i++)
	{
		cin >> a[i];
		if(vis[a[i]] == 1)	ans++;//保证和在后面 
		//如果把这一行判断单独拿出来,则无法保证先后顺序
		for(int j=0;j<i;j++)
		{
			vis[a[i]+a[j]] = 1;
		}
	}
	cout << ans << endl;
	return 0;
}

问题 F: 叠罗汉

题目描述
农场的N头奶牛喜欢玩叠罗汉游戏,就是几头奶牛1头奶牛接着1头奶牛的站成一柱子形状。不过奶牛的力量不一样,用数值Ci表示第i头奶牛它的上面最多可以站多少头奶牛,问这些奶牛最少可以站成几个柱子形状。
输入
第一行1个整数N,表示有多少头奶牛。1<=N<=1000。
第二行N个正整数Ci,表示这些奶牛的力量。0<=Ci<=1000。

输出
一个整数,表示最少成几个“罗汉”。
样例输入 Copy
5
0 2 1 2 2
样例输出 Copy
2
提示
可以第1、第3、第2头奶牛从上向下叠罗汉; 第4、第5头奶牛叠罗汉。

思路:
有点贪心的思想,让力量大的放在底部。把每一个力量的数量记录一下,遍历力量,如果存在比当前力量大的并且满足这个数大于等于上面已经叠好的,那么这一组罗汉的个数就可以加一,同时记录这个力量的数组减一

int n,x,ans,cnt;
int vis[1005];
int main()
{
	cin >> n;
	for(int i=0;i<n;i++)
	{
		cin >> x;
		vis[x]++;
	}
	for(int i=0;i<=1000;i++)
	{
		while(vis[i] > 0)//存在这个力量 
		{
			ans++;
			cnt = 0;
			for(int j=i;j<=1000;j++)
			//从这个力量往上,判断有没有比当前力量更大的,如果有,就可以放在下面 
			{
				while(vis[j] > 0 && j >= cnt)
				{
					vis[j]--;//用过一个之后就减少	
					cnt++;//在最下面放了一个就+1 
				}
			}
		}
	}
	cout << ans << endl; 
	return 0;
}

问题 H: 掰手腕

题目描述
编程集训,好累人!
课间,有N个人闹着要掰手腕比赛,时间有限,只进行K场对弈。
每个人最多参加两场对弈,最少参加零场对弈。
每个人都有一个与其他人都不相同的等级(用一个正整数来表示)。
在对弈中,等级高的人在裁判的右边,等级低的人在裁判的左边。
每一个人最多只能在左边和右边各一次。
为了增加比赛的可观度,观众希望K场对弈中双方的等级差的总和最小。
比如有7个选手,他们的等级分别是30,17,26,41,19,38,18,要进行3场比赛。最好的安排是Player 2 vs Player 7, Player 7 vs Player 5 , Player 6 vs Player 4,此时等级差的总和等于(18 - 17) + (19 - 18) + (41 - 38) = 5达到最小。
输入
第一行两个正整数N,K;
接下来有N行,第i行表示第i+1个人等级。
输出
在第一行输出最小的等级差的总和。
样例输入 Copy
7 3
30
17
26
41
19
38
18
样例输出 Copy
5
提示
在90%的数据中,1 ≤ N ≤ 3000;
在100%的数据中,1 ≤ N ≤ 100000;
保证所有输入数据中等级的值小于10^8,1 ≤ K ≤ N-1 。

思路:
首先对原数据排序,把每两个数之间的差值保存下来,对差值再排序,将前k个加和即可

int n,k,ans,cnt;
int a[maxn],b[2*maxn];
int main()
{
	cin >> n >> k;
	for(int i=0;i<n;i++)	cin >> a[i];
	sort(a,a+n);
	for(int i=0;i<n-1;i++)
		b[cnt++] = a[i+1]-a[i];
	sort(b,b+cnt);
	for(int i=0;i<k;i++)
		ans += b[i];
	printf("%d\n",ans);
	return 0;
}

问题 I: 踢石头

题目描述
编程集训,真累人!
课间,某君在操场上沿着笔直的跑道从左往右走着、散步休息……
看到有N个排成一直线的小石头,于是他无聊的踢了起来:
在他遇到第“奇数”块石头时,他会将其往前面踢,能踢多远在输入中会给出,而遇到第“偶数”个石头时不进行处理(不踢:略过)。当有多个石头在同一位置时,则先处理“射程”(踢下,石头往前移的距离)最短的石头。
然后他就这么一直往前走,边走边踢,直到前面已经没有任何石头时,这时候计算该群与出发点的距离。
输入
第一行一个正整数N (0 接下来有N行,每行两个整数,分别表示石头的位置Pi及其射程Di。Pi(0<=Pi<=100,000) 、Di(0<=Di<=1,000)
输出
前面没石头时,离出发点的距离
样例输入 Copy
【样例1】
2
1 5
2 4
【样例2】
2
1 5
6 6
样例输出 Copy
【样例1】
11
【样例2】
12
提示
样例1解析
一开始的时候遇到的是第一个石头(1,5),他的坐标是1,然后往前踢了5个单位之后,坐标变成(6,5) 随后继续往前走,开始遇到第二个石头(2,4),忽略过。 然后继续往前走,遇到了第三个石头(6,5),即原第一个石头,但是它此时坐标为6。往前踢了5个单位之后,坐标变成(11,5), 继续往前走,一直走到坐标11时,遇到第四个石头(11,5),忽略。 前面已经没有石头了,因此此时离坐标原点的距离为11。

在90%的数据中,1 ≤ N ≤ 3000;
在100%的数据中,1 ≤ N ≤ 100000;
保证所有输入数据中等级的值小于10^8,1 ≤ K ≤ N-1 。

思路:
我们可以运用优先队列解决上述问题,先将每个石头的位置及其能够扔的距离封装成一个结构体。优先队列的优先级即可。利用一个bool变量来控制奇偶,奇数时处理,偶数时不处理。最后的一个石头的坐标即是能扔的最远的距离

ll n;
struct node
{
	ll p;
	ll d;
	friend bool operator<(node a,node b)
	{
		if(a.p == b.p)	return a.d > b.d;
		return a.p > b.p;
	}
};

int main()
{
	cin >> n;	
	node a;
	priority_queue<node> q;
	for(int i=0;i<n;i++)
	{
		cin >> a.p >> a.d;
		q.push(a);
	}
	bool temp = true;//切换奇偶 
	while(!q.empty())
	{
		a = q.top();	q.pop();
		if(temp)
		{
			a.p += a.d;
			q.push(a);//踢出去的石头放回队列 
		}
		temp = !temp;
	}
	cout << a.p << endl;
	return 0;
}

你可能感兴趣的:(upc第一阶段训练)