第一周题单

A.

找两个最大的长度为k的区间和
默认当前区间是最大或第二大的,然后找第二大的或最大的
若当前区间不是以上任一种,则它们的结果不重要
这样避免了很多重复的情况

#include <bits/stdc++.h>
using namespace std;

#define int long long
void solve(){
    int n,k;cin>>n>>k;
    vector<int>a(n+1),pre(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
        pre[i]=pre[i-1]+a[i];
    }
    int res=-1e18,ma=-1e18;
    for(int i=k;i+k<=n;i++){
        ma=max(ma,pre[i]-pre[i-k]);
        res=max(res,ma+pre[i+k]-pre[i]);
    }
    cout<<res<<'\n';
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int hey_left=1;
    cin>>hey_left;
    while(hey_left--){
        solve();
    }
}

B.

消耗是b-a,把消耗大等于0的排在前面,在内部,按a从小到大排
消耗小于0的:内部按b从大到小排

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 5;
struct Node {
    ll a,b,c;
}pp[maxn];
bool cmp(const Node &A,const Node &B)
{
    if(A.c>=0&&B.c<0) return true;
    if(A.c<0&&B.c>=0) return false;
    if(A.c>=0&&B.c>=0) return A.a<B.a;
    return A.b>B.b;  
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,flag=0;
        ll m;
        scanf("%d%lld",&n,&m);
        for(int i=1;i<=n;i++) scanf("%lld%lld",&pp[i].a,&pp[i].b),pp[i].c=pp[i].b-pp[i].a;
        sort(pp+1,pp+1+n,cmp);
        for(int i=1;i<=n;i++) {
            if(m<pp[i].a) {
                flag=1; break;
            }
            m+=pp[i].c;
        }
        if(!flag) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

C.

1.输入,并准备4个数组,分别用来存储地毯的坐标以及长度。
2.可以从最后一个地毯开始遍历,从最后一个地毯开始遍历的好处就是,第一个遇到的在那个点上面的就是最上层的,即我们所需要的答案,所以我们可以定义一个变量表示编号,没有就是-1,所以赋初值为-1。

#include <bits/stdc++.h>
 
using namespace std;
 
int main(){
    int n;
    cin>>n;
    int a[101000],b[101000],c[101000],d[101000]; //分别存储坐标值和长度
    for (int i=1;i<=n;i++){
        scanf("%d%d%d%d",&a[i],&b[i],&c[i],&d[i]);
    }
    int num = -1;
    int x,y;
    cin>>x>>y;
    // 从最后一个开始遍历
    for (int i=n;i>=1;i--){
        // 因为给的是长度和宽度,所以需要加上其x,y的坐标值,求其他几个点坐标的x,y值
        if (a[i]<=x&&b[i]<=y&&c[i]+a[i]>=x&&d[i]+b[i]>=y){
            num = i;
            break;
        }
    }
    cout<<num;
    return 0;
}

D.

排序
把最大最小的组合在一起,不能组合就大的单独一组

#include <stdio.h>

#include <algorithm>

using namespace std;

int main()

{
int g;
scanf("%d",&g);
   int n;
   scanf("%d",&n);
   int p[30001];
   int i,j,tmp;
   for (i=0;i<n;i++)
   {
scanf("%d",&p[i]);
   }
   sort(p,p+n);      // 纪念品价格从小到大排列
   int ans=0;
   i=0;  j=n-1;
   while (i<j)
   {
       if (p[i]+p[j]>g) j--;
       else
       {
           i++; j--;
       }
       ans++;
   }
   if (i==j) ans++;
   printf("%d\n",ans);
   return 0;
}

E.

把每个区域标记,统计未标记的个数

#include<stdio.h>
#define N 10001
int main()
{
    int l,m,i,j,sum=0,a[N],b[N],c[N]={0};
    //c[N]数组用来表示树木,初始值均为零,表示还没有被移走 
    scanf("%d%d",&l,&m);//输入马路长度和区域数 
    for(i=0;i<m;i++) 
    scanf("%d%d",&a[i],&b[i]);//输入每组数据的起始点 
    for(i=0;i<m;i++)
    {
        if(a[i]>b[i])
        {
            for(j=b[i];j<=a[i];j++)
            c[j]=1;
        }
        if(a[i]<=b[i])
        {
            for(j=a[i];j<=b[i];j++)
            c[j]=1;
        }
    } //由于不确定是否严格按照起点和终点的格式输入,所以要先判断大小。
    //在区域范围内的树木标记为1 
    for(i=0;i<=l;i++)
    if(c[i]==0) 
    sum=sum+1;
    //判断,没有被标记的树木c[i]=0,总数+1 
    printf("%d",sum);
    return 0;
}

F.

一个set搞定

#include <bits/stdc++.h>
using namespace std;

#define int long long

void solve(){
    int n;cin>>n;
    set<int>st;
    for(int i=1,x;i<=n;i++){
        cin>>x;
        st.insert(x);
    }
    cout<<st.size()<<'\n';
    for(int t:st){
        cout<<t<<' ';
    }
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int hey_left=1;
    //cin>>hey_left;
    while(hey_left--){
        solve();
    }
}

G.

转成字符串比较较为方便
注意把字符串拼接后再比较

#include<iostream>
#include<algorithm>
using namespace std;
int cmp(string a,string b)//定义字符串排序
{
    if(a+b>b+a)return 1;
    else return 0;
}
int main()
{
	int n;
	string a[25];
	cin>>n;
	for(int i=1;i<=n;i++)
		cin>>a[i];
	sort(a+1,a+1+n,cmp);
	for(int i=1;i<=n;i++)
		cout<<a[i];
	return 0;
}

H.

二维前缀和

#include <iostream>
#include <cstdio>
using namespace std;
#define inf 0x3fffffff

const int maxn = 5e3 + 5;

int a[maxn][maxn];

int main(void) {
    int n, r;
    scanf("%d%d", &n, &r);

    int x, y, c, xx, yy;
    xx = yy = r;
    for(int i = 1; i <= n; i++) {
        scanf("%d%d%d", &x, &y, &c);
        a[++x][++y] += c;
        xx = max(xx, x);
        yy = max(yy, y);    
    }

    for(int i = 1; i <= xx; i++) 
        for(int j = 1; j <= yy; j++)
            a[i][j] = a[i - 1][j] + a[i][j - 1] - a[i - 1][j - 1] + a[i][j];

    int ans = -inf;
    for(int i = r; i <= xx; i++) 
        for(int j = r; j <= yy; j++) 
            ans = max(ans, a[i][j] - a[i - r][j] - a[i][j - r] + a[i - r][j - r]);

    printf("%d\n",ans);

    return 0;
} 

I.

差分,前缀和

#include <iostream>
#include <vector>
using namespace std;
 
int main()
{
    int l,m,ans = 0,x,y,i;
	cin >> l >> m;
    vector<int> v1(l+5,0),v2(l+5,0);
    //v1有树时为0,v2差分
	for(i = 1;i <= m;i++)
	{
        cin >> x >> y;
		v2[x]++;
		v2[y+1]--;
	}
    v1[0] = v2[0];
	if(v1[0] == 0) {
        ans++;
    } 
	for(i = 1;i <= l;i++)
	{
		v1[i] = v1[i-1]+v2[i];
		if(v1[i] == 0) {
            ans++;
        }
	}
	cout << ans;
	return 0;
 } 

J.

贪心

#include<bits/stdc++.h>
using namespace std;
const int 
	N=50005;
int n;
struct node{int l,r;}a[N];
bool cmp(node x,node y){return x.r<y.r;}
int main(){
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d%d",&a[i].l,&a[i].r);
	sort(a+1,a+1+n,cmp);
	int l=a[1].l,r=a[1].r,ans=1;
	for (int i=2;i<=n;i++)
		if (a[i].l>=r) l=a[i].l,r=a[i].r,ans++;
	printf("%d\n",ans);
	return 0;
}

K.

选与不选的问题,01串枚举

#include<bits/stdc++.h>
using namespace std;
int n, m ,k;
int a[20][20];
long long sh[20],sl[20];//sh[i]是第i行的和,sl[i]是第i列的和(在行选完之后用)
bool b[20];//b[i]是01串转换过来的bool数组(用bool数组主要是为了方便大家理解)
long long ans = 0; //存答案,注意总和会超过int的最大值要用long long
int deal(int x)  //把x这个数字转换成bool数组
{
    memset(b, 0, sizeof(b)); //不清数组会死!
    int cnt = 0, i = 1; //cnt存01串里面1的个数
    while (x)
    {
        if (x&1)  //如果x的最后一位是1
        {
            cnt++;//记录其中1的数量 
            b[i] =1;//说明第i行是被选择的 
        }
        x>>=1;  //x右移一位,等价于除以二取整
        i++;//到下一行 
    }
    return cnt;
}
int main()
{
//————————————读入——————————————
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; i++)
    for (int j = 1; j <= m; j++)
    {
        scanf("%d", &a[i][j]);
        sh[i] += a[i][j];//记录每一行的值 
    }
//————————以上读入不多说————————
 
//——如果k>n或者k>M,那么肯定就把矩阵选完了————
//这里我这么处理主要是因为我后面的有个判断把这种情况给直接continue掉了,
//debug的时候不想的再写别的了就这样了
//k=n或者m就已经能把矩阵选完,所以结果肯定是一样的
    if (k > n) k = n;
    if (k > m) k = m;
//————————————————————————————————————————
 
//—————枚举选行的所有可能性,贪心处理列——————
    int tmp = (1<<n) -1;  //1<<n就等于2^n,位运算不会的同学百度一下
    for (int T =0; T <= tmp; T++)  //T用来枚举行的状态
    {
        int numh = deal(T);//将状态T转为bool数组,并返回有多少个1,既要选多少行
        int numl = k-numh; //numl是要选多少列
        if (numl > m || numl < 0) continue;
        //numl>m或者numl<0,说明行的方案不对(其实也有可能是k太大,这里就是上文说的k比较大的时候直接continue没了的地方)。
         
        long long  sum=0;  //sum是本次枚举的方案的和
        for (int i = 1; i <= n; i++) 
            if (b[i]) sum += sh[i]; //把选中的行都加上
 
        memset(sl, 0, sizeof(sl));//行选得不同矩阵就不同,所以sl数组每次都要清干净
        for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            if (!b[i]) sl[j] += a[i][j]; //第i行没有选,j列的和需要加上a[i][j](第i行选了a[i][j]就清零了不能加)
        sort(sl+1, sl+1+m);//对每一列的和进行排序(刨去指定行) 
        for (int i=1,j=m; i<=numl; i++,j--) sum += sl[j]; //把最大的numl列的和加到方案里
        ans = max(ans, sum);//维护答案
    }
//————————————枚举+贪心结束——————————————
    printf("%lld\n", ans);
    return 0;
 
}

posted @ 2024-01-22 22:15  WW爆米花  阅读(4)  评论(0编辑  收藏  举报