第一周题单
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;
}