高精度前缀和差分二分贪心队内题解报告

第一题求最大子矩阵和
可以转化成最大子序列和来做,用i,k表示行数,将第i行到第k行每一列数依次相加,存储在数组sum[]中,求出sum[]的最大子序列和。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,a[105][105],dp[105],sum[105],max1;
int main()
{
    
    
    //freopen("D://in.txt","r",stdin);
    while(~scanf("%d",&n))
    {
    
    
        max1=-0xFFFFF;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                scanf("%d",&a[i][j]);
        for(int i=0;i<n;i++)
        {
    
    
            memset(sum,0,sizeof(sum));
            for(int k=i;k<n;k++)
            {
    
    
                for(int j=0;j<n;j++)
                {
    
    
                    sum[j]+=a[k][j];
                    dp[j]=-0xFFFFF;
                }
                dp[0]=sum[0];
                max1=max(max1,dp[0]);
                fornt j=1;j<n;j++)
                {
    
    
                    dp[j]=max(sum[j],dp[j-1]+sum[j]);
                    max1=max(max1,dp[j]);
                }
            }
        }
        printf("%d\n",max1);
    }
    return 0;
}

第二题给你一个m×n的整数矩阵,在上面找一个x×y的子矩阵,使子矩阵中所有元素的和最大
用二维数组map[i][j]表示前i行第j个数的和,把前i行的和压缩到第i行上。

#include<cstdio>
#include<string.h>
int map[1010][1010];
int main()
{
    
    
    int t;
    int x,y,m,n,a,sum,max0;
    scanf("%d",&t);
    while(t--)
    {
    
    
        memset(map,0,sizeof(map));
        scanf("%d %d %d %d",&m,&n,&x,&y);
        for(int i = 1 ; i <= m ; i++)
        {
    
    
            for(int j = 1 ; j <= n ; j++)
            {
    
    
                scanf("%d",&a);
                map[i][j]=map[i-1][j]+a;                //map[i][j]表示前i行第j个数的和
            }
        }
        max0=0;
        for(int i = x ; i <= m ; i++)                    //i从第x行开始(第x行的值表示序列前x行的和)
        {
    
    
            for(int j = y ; j <= n ; j++)
            {
    
    
                sum=0;
                for(int k = j-y+1 ; k <= j ; k++)        //控制k的范围为y
                {
    
    
                    sum+=(map[i][k]-map[i-x][k]);        //求出x*y矩阵的和
                }
                if(sum > max0)
                {
    
    
                    max0=sum;
                }
            }
        }
        printf("%d\n",max0);
    }
}

第三题Give you three sequences of numbers A, B, C, then we give you a number X. Now you need to calculate if you can find the three numbers Ai, Bj, Ck, which satisfy the formula Ai+Bj+Ck = X.
定义三个数组分别存a,b,c的值,再定义数组d存a和b所有可能的和,用给定的数减去c数组中的数得到k,用二分法在d数组找k值,若能找到输出yes,否则输出no。

#include<cstdio>
#include<algorithm>
using namespace std;
int a[505],b[505],c[505],d[505*505];
int main()
{
    
    
    int j,l,p,m,n,i,k,t,s,num,r,le,mid,key;
    num=0;
    while(scanf("%d %d %d",&l,&m,&n)!=EOF)
    {
    
    
        for(i = 0 ; i < l ; i++)
        {
    
    
            scanf("%d",&a[i]);
        }
        for(i = 0 ; i < m ; i++)
        {
    
    
            scanf("%d",&b[i]);
        }
        for(i = 0 ; i < n ; i++)
        {
    
    
            scanf("%d",&c[i]);
        }
        p=0;
        for(i = 0 ; i < l ; i++)
        {
    
    
            for(j = 0 ; j < m ; j++)
            {
    
    
                d[p++]=a[i]+b[j];
            }
        }
        sort(c,c+n);
        sort(d,d+p);
        scanf("%d",&t);
        printf("Case %d:\n",++num);
        while(t--)
        {
    
    
            scanf("%d",&s);
            key=0;
            for(i = 0 ; i < n ; i++)
            {
    
    
                k=s-c[i];
                le=0;
                r=p-1;
                while(le <= r)
                {
    
    
                    mid=(le+r)/2;
                    if(k == d[mid])
                    {
    
    
                        key=-1;
                        break;
                    }
                    if(k > d[mid])
                    {
    
    
                        le=mid+1;
                    }
                    else
                    {
    
    
                        r=mid-1;
                    }
                  }
                 if(key == -1) break;
            }
            if(key == -1) printf("YES\n");
            else
                printf("NO\n");
        }
    }
}

第四题

若出发的房间号小于到达的房间号,正常记录,否则需要交换一下,也看成是从房间号小的房间移动到大的房间; 
出发房间为偶数则减一,结束房间为奇数则加一,想想为什么
#include<iostream>
#include<iomanip>
#include<algorithm> 
#include<cstring>
#include<sstream> 
#include<cmath>
#define NUM 20010
int addTime=10;
using namespace std;
int main()
{
    
    
	int t,n,low,high,temp,ans,maxNum;
	int room[NUM]; 
	cin >> t;
	while(t--){
    
    
		memset(room, 0, sizeof(room));
		cin >> n;
		maxNum=0;
		for(int i=0; i<n; i++){
    
    
			cin >> low >> high;
			if(low > high){
    
    
				temp = low;
				low = high;
				high = temp;
			}
			if(maxNum<high)
				maxNum=high;
			if(low%2==0)
				low-=1;
			if(high%2==1)
				high+=1;
			for(int j=low; j<=high; j++)
				room[j] += addTime;
		}
		ans=0;
		for(int i=0; i<=maxNum; i++)
			if(room[i]>ans)
				ans=room[i];
		cout << ans << endl;
	}
	return 0;
}

第五题1.题意:给定很多间隔,合并间隔,使得合并后间隔最小。
思路:先用sort函数按照间隔前段由小到大排序,然后判断是否可以合并,

#include<stdio.h>
#include<algorithm>
#include<iostream>
using namespace std;
#define max 0x7fffffff;
struct node
{
    
    
    int start;
    int end;
} a[50000];
int cmp(struct node a,struct node b)
{
    
    
    return a.start<b.start;
}
int main()
{
    
    
    int n,ta,tb;
    scanf("%d",&n);
    for(int i=0; i<n; i++)
    {
    
    
        scanf("%d%d",&a[i].start,&a[i].end);
    }
    sort(a,a+n,cmp);
   ta=a[0].start;
   tb=a[0].end;
   a[n].start=max;
   a[n].end=max;
    for(int i=1; i<=n; i++)
    {
    
    
        if(tb<a[i].start)
        {
    
    
            printf("%d %d\n",ta,tb);
            ta=a[i].start;
            tb=a[i].end;
        }
        /*else if(tb>=a[i].end)
        {
            tb=a[i].end;
        }*/
        else if(tb>=a[i].start&&tb<=a[i].end)
        {
    
    
            tb=a[i].end;
        }
    }
    return 0;
}

第六题

题意:假设海岸线是一条无限延伸的直线。陆地在海岸线的一侧,而海洋在另一侧。每一个小的岛屿是海洋上的一个点。雷达坐落于海岸线上,只能覆盖d距离,所以如果小岛能够被覆盖到的话,它们之间的距离最多为d。

题目要求计算出能够覆盖给出的所有岛屿的最少雷达数目。

我们假设岛屿i它的x坐标为island[i][0],而y坐标为island[i][1],那么有以下几种情况是invalide的,即输出-1的情况:

1.island[i][1]<0

2.abs(island[i][1])<d

3.d<0

其他的情况,应该就是正常情况,进入计算最小雷达数目。

在这里插入图片描述

如上图,红色的点为岛屿,那么能够覆盖到此岛屿的雷达所在的区间,应该就是以该岛屿为圆心的圆与x轴交点所在的区间。

这样,我们就可以计算出所有岛屿的雷达所在的区间,得到一个区间数组。

我们将这个数组按照区间左部分进行排序,那么重叠部分就表明这些岛屿的雷达可以共用一个。从而计算出最终解。

#include<stdio.h>
#include<limits.h>
#include<math.h>
#define MAXNUM 12701
//以岛屿为圆心,以d为半径做圆;得到各个圆与x轴相交的区间;去掉重复区间,即得到雷达数目 
int calMin(int **island,int n,int d);
int main() {
    
    
    
    int num = 0;
    char s[10];
    
    while(1){
    
    
             
          num++;
          int n,d,i;
          scanf("%d%d",&n,&d);
          
          if(n==0&&d==0){
    
    
          
                break;               
          }         
          
          int **island = (int**)malloc(sizeof(int*)*n);
          for(i=0;i<n;i++){
    
    
                           
              island[i] = (int*)malloc(sizeof(int)*2);
              scanf("%d%d",&island[i][0],&island[i][1]);
          }
          
          //计算最少雷达个数 
          int min = calMin(island,n,d);
          printf("Case %d: %d\n",num,min);
          
          //读取空白行
          gets(s); 
          
          free(island);
    }
    
    system("pause");
    return 0;
}
int calMin(int **island,int n,int d){
    
    
    
    double *arr = (double*)malloc(sizeof(double)*n*2);
    int i,j;
    
    for(i=0;i<n;i++){
    
    
            
         if(island[i][1]<0||island[i][1]>d||d<0){
    
    
                                                   
              return -1;
         }
         
         //计算左右区间
         if(abs(island[i][1]-d)<1e-6){
    
    
                                      
            arr[2*i] = island[i][0];
            arr[2*i+1] = island[i][0];                             
         }else{
    
    
               
             //计算
             double x = sqrt(pow(d,2)-pow(island[i][1],2)); 
             
             arr[2*i] = (double)island[i][0] - x;
             arr[2*i+1] = (double)island[i][0] + x;     
         }
    }
    
    //排序
    for(i=0;i<n-1;i++){
    
    
    
        for(j=0;j<n-i-1;j++){
    
    
                             
              if(arr[2*j]>arr[2*(j+1)]){
    
    
                                        
                   double temp = arr[2*(j+1)];
                   arr[2*(j+1)] = arr[2*j];
                   arr[2*j] = temp;
                   
                   temp = arr[2*(j+1)+1];
                   arr[2*(j+1)+1] = arr[2*j+1];
                   arr[2*j+1] = temp;                          
              }                     
        }        
    } 
    
    //去掉重合区间,得到雷达个数
    int num = 0;
    double right = -1;
    
    for(i=0;i<n;i++){
    
    
                     
        if(i==0){
    
    
           
            num++; 
            right = arr[2*i+1];        
        }else{
    
    
              
            if(arr[2*i]<=right){
    
    
                             
                  if(arr[2*i+1]<right){
    
    
                                       
                     right = arr[2*i+1];                     
                  }         
                  continue;                            
            }else{
    
    
                  
                  num++;
                  right = arr[2*i+1];      
            }
        }
    } 
    
    return num;
}

第七题题目要求从每一列挑出一个数加起来总和为0,直接暴力解会超时,于是有两种优化的方法,分别是

(1)先将第一二列和第三四列分别加起来求出总和,然后将两组总和排序,一组从低开始,一组从高开始互加;

(2)先将第一二列和第三四列分别加起来求出总和,将其中一组排序后,遍历另外一组,通过二分挑选那个排列好的总和数组来相加

#include<cstdio>
#include<algorithm>
using namespace std;
int a[4001],b[4001],c[4001],d[4001];
int ab[4000*4000+1],cd[4000*4000+1];
int main()
{
    
    
    int n,i,j,k,num,sum,x;
    while (~scanf("%d",&n))
    {
    
    
        for (i=0;i<n;i++)
            scanf("%d %d %d %d",&a[i],&b[i],&c[i],&d[i]);
        k=0;
        for (i=0;i<n;i++){
    
    
            for (j=0;j<n;j++)
               ab[k++]=a[i]+b[j];
        }
        k=0;
        for (i=0;i<n;i++){
    
    
            for (j=0;j<n;j++)
               cd[k++]=c[i]+d[j];
        }
        sort(ab,ab+n*n);
        sort(cd,cd+n*n);
        x=n*n-1;sum=0;
        for (i=0;i<n*n;i++)
        {
    
    
            while (x>=0&&ab[i]+cd[x]>0)
                x--;
            if (x<0)
                break;
            num=x;
            while (ab[i]+cd[num]==0&&num>=0)
            {
    
    
                sum++;
                num--;
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}

第八题用一个优先队列进行模拟(priority_queue)order的情况。
先按照截止日期due对Order进行从小到大的排序,用pass表示当前完成最多订单需要的最短的时间,
遍历Order,当pass + order[i].q <= order[i].d的时候,
表示可以正常完成该订单,进队,同时pass += order[i].q,
如果pass + order[i].q > order[i].d的时候,
则需要考虑order[i].q和队列中需要最长时间的订单之间的关系,
如果order[i].q较大,说明该订单不可能完成,否则入队,pass += order[i].q,
然后要减去队列中需要最长时间的订单(即队首),一直贪心,最后留在队列中的订单个数就是保留的订单个数。

#include<cstdio>
#include<queue>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#define N 800000+5
using namespace std;
struct g{
    
    
	int cost;
	int date;
}a[N];
int n;
int cmp(const void *a,const void *b){
    
    
	return (*(struct g *)a).date>(*(struct g *)b).date?1:-1;
}
int main(){
    
    
	int i;
	while(scanf("%d",&n)==1){
    
    
		for(i=0;i<n;i++)
			scanf("%d%d",&a[i].cost,&a[i].date);
		qsort(a,n,sizeof(a[0]),cmp);
		 priority_queue<int> q;
		int ans=0;
		for(i=0;i<n;i++){
    
    
			if(ans+a[i].cost<=a[i].date){
    
    
				ans+=a[i].cost;
				q.push(a[i].cost);
			}
			else
				if(ans+a[i].cost>a[i].date&&!q.empty()&&a[i].cost<q.top()){
    
    
					ans+=a[i].cost;
					q.push(a[i].cost);
				}
				if(ans>a[i].date){
    
    
					ans-=q.top();
					q.pop();
				}
		}
		printf("%d\n",q.size());
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46264636/article/details/106452098