/*Java Cinema Cashier分析题解_树状数组的应用

1. 题目

All cinema halls in Berland are rectangles with K rows of K seats each, and K is an odd number. Rows and seats are numbered from 1 to K. For safety reasons people, who come to the box office to buy tickets, are not allowed to choose seats themselves. Formerly the choice was made by a cashier, but now this is the responsibility of a special seating program. It was found out that the large majority of Berland's inhabitants go to the cinema in order to watch a movie, that's why they want to sit as close to the hall center as possible. Moreover, a company of M people, who come to watch a movie, want necessarily to occupy M successive seats in one row. Let's formulate the algorithm, according to which the program chooses seats and sells tickets. As the request for M seats comes, the program should determine the row number x and the segment [yl, yr] of the seats numbers in this row, where yr - yl + 1 = M. From all such possible variants as a final result the program should choose the one with the minimum function value of total seats remoteness from the center. Say, 

 — the row and the seat numbers of the most "central" seat. Then the function value of seats remoteness from the hall center is 

. If the amount of minimum function values is more than one, the program should choose the one that is closer to the screen (i.e. the row number x is lower). If the variants are still multiple, it should choose the one with the minimum yl. If you did not get yet, your task is to simulate the work of this program.

Input

The first line contains two integers N and K (1 ≤ N ≤ 1000, 1 ≤ K ≤ 99) — the amount of requests and the hall size respectively. The second line contains N space-separated integers Mi from the range [1, K] — requests to the program.

Output

Output N lines. In the i-th line output «-1» (without quotes), if it is impossible to find Mi successive seats in one row, otherwise output three numbers x, yl, yr. Separate the numbers with a space.

2. Cinema Cashier分析题解

        1) 价钱是同一波来的人算一次价钱,让这个价钱最小;如果有价钱相同的座位安排,取左上角的座位安排。为满足此要求,最好是每次都从第一排第一个位置开始循环找连座儿 (贪心),使找到的第一个价钱最小的位置区间就能满足左上角。这样只用往后找最低价对应的座位区间就OK。

        2) 注意每当一波人的座位安排确定了,都要把座位情况更新;另外,一旦某次m个人想坐的位置区间上有一个不为空,这个位置区间就不满足要求。

        根据上面的分析可以抽象出k个树状数组,分别存储每排的座位情况。(1代表已有人坐,0代表未有人坐,初始情况全为0)。

         用 add(line, x, k) query(line, l, r)方法进行更新求区间和 (求区间和还要先用sum(line, x)求前缀和) 。

        区间和干嘛用呢?" m个人某次考虑的位置区间上有一个不为空" 即此位置区间(line, l, r)区间和 query(line, l, r) !=0 这样就可以用这段区间的区间和判断是否可坐。

        下面是代码和注释,好不容易才搞完的:

//所有注释都在被解释语句的右侧或下面
package homework3;
import java.util.*;
public class Cinema_Cashier {
	static int[][]tree=new int[102][102];
	/*n代表有几波人,n波人就要求n个区间和
	 * 高效求区间和就维护了一个树状数组
	 * 第一个括号代表第n波人所坐排数
	 * 第二个括号则是树状数组内存的东西
	 */
	public static int lowbit(int x)
	/*lowbit()方法,求最低位1的值*/
	{
		return x&(-x);
		/*这个是二进数x和其负值(即反码+1)的与(只保留相同位)*/
	}
	public static int sum(int line, int x)
	/*用树状数组求前缀和*/
	{
		int res=0; //初始前缀和为0
		for(int i=x;i>=1;i=i-lowbit(i))
		/*循环是根据树状数组的特点求,最好对照着图
		 * 比如求k项的前缀和,只要把C[K]项的左上的所有结点C[i]相加即可
		 * 而"左上所有结点"的i值与k的关系由lowbit(i)获得
		 */
			res+=tree[line][i];
		return res;
	}
	
	public static int query(int line, int l, int r)
	/*求l到r的区间和*/
	{
		return sum(line,r)-sum(line,l-1);
	}
	
	public static void add(int line, int x, int k)
	/*修改&初始化树状数组*/
	{
		for(int i=x;i<=k;i=i+lowbit(i))
			tree[line][i]++;
	}
	
	public static int cost(int l, int r)
	/*就是求个等差数列的区间和*/
	{
		return (l+r)*(r-l+1)>>1;
		/*妈的我以为什么高深的东西,直接写/2不好吗???
		 * 这里(l+r)*(r-l+1)一定是偶数,所以用了移位
		 * 要是奇数就不能移位了哦
		 */
	}
	
	public static void main(String[] args) {
		Scanner s=new Scanner(System.in);
		int n=s.nextInt(); //几波人
		int k=s.nextInt(); //座位正方形边长
		int mid=(k+1)/2; //中间位置下标
		
		for(int i=1;i<=n;i++)
			/*第i波人*/
		{
			int m=s.nextInt(); //第i波有m个人
			int ansx=-1, ansy=-1; //记录第i波人所坐位置
			int minn=0x3f3f3f3f; //初始化成一个快要正无穷的东西(运行中不会出现的值)
			for(int x=1;x<=k;x++)
			/*第x排*/
				for(int y=1;y+m-1<=k;y++)
				/*第x排从第y个开始到第y+m-1个
				 * 循环找出既能坐价钱又最低的位置区间
				 * 循环条件是不能越界
				 */
				{
					if(query(x,y,y+m-1)==0)
					/*第x排从第y个开始到第y+m-1个位置可用
					 * 本次的y到y+m-1个座位中只要有一个不为零就做不了这波的m个人
					 * 即只要query求得的区间和不为0就不能坐下,要换位置
					 */
					{
						int tmp; //价钱
						if(y>=mid) tmp=cost(y,y+m-1)-mid*m+Math.abs(x-mid)*m;
						else if(y+m-1<=mid) tmp=mid*m-cost(y,y+m-1)+Math.abs(x-mid)*m;
						else tmp=Math.abs(x-mid)*m+cost(mid,y+m-1)-(y+m-mid)*mid+mid*(mid-y)-cost(y,mid-1);
						if(tmp<minn)
						{
							minn=tmp; //价钱更小就换掉
							ansx=x; //ansx记上座位
							ansy=y;
						}
					}
				}
			
			if(minn!=0x3f3f3f3f)
			{
				System.out.printf("%d %d %d\n",ansx,ansy,ansy+m-1);
	            for(int j=ansy;j<=ansy+m-1;j++)
	                add(ansx,j,k); 
	            	/*改变树状数组的值,把已被占用位置变为1
	            	 * 下次循环判断位置是否可用时座位已更新
	            	 */
			}
			else System.out.println("-1");
		}		
	}
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李 董

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值