目录
2021省赛
砝码称重
异或数列
2020省赛
七段码
子串分值
2019省赛
迷宫
外卖店优先级
修改数组
糖果
2020国赛
B 含2天数
C 本质上升序列
E 玩具蛇
H 答疑
砝码称重https://www.acwing.com/problem/content/3420
略有变形的01背包问题
先把所有砝码可能达到的最大总重sum计算出来,每次多讨论一个砝码时枚举1~sum之间的所有数,如果被标记过(表明之前已经得到过这个重量),我们可以在此数基础上加上当前砝码重量/减去当前砝码重量,并对得到的数值进行标记,并把当前讨论的砝码的单独的重量打上标记,这样一层一层推进。
#include
using namespace std;
int n,a[110],ok[100010][110],sum;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum+=a[i];
}
ok[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=sum;j++)
{
if(ok[j][i-1])
{
ok[j+a[i]][i]=1;
ok[abs(j-a[i])][i]=1;
ok[j][i]=1;
}
}
ok[a[i]][i]=1;
}
int ans=0;
for(int i=1;i<=sum;i++)
if(ok[i][n]) ans++;
cout<
异或数列https://www.acwing.com/problem/content/3424/
博弈问题
先判断一下每组数内的所有数是否都出现两次(异或和为0),此时为平局情况。
若不为平局,则要比谁最后可以拿到二进制下最高位的有效的1。
于是先遍历所有数,按二进制数位统计每个数位上1的个数——
如果最高位的1有偶数个:无效,直接比较次最高位;
如果最高位的1有奇数个:
特判:如果最高位1只对应1个数,这个数一定会率先被拿走,所以是先手胜出;
若n的总数为奇数个,先手先拿最高位是1的数,若后手拿最高位是0的数,先手就拿最高位是0的数,若后手拿最高位是1的数,先手就拿最高位是1的数,最后是先手胜出;
若n的总数为偶数个,则含有奇数个最高位是0的数,后手有办法拿到最高位奇数个0和奇数个1,先手只能拿偶数个最高位1,后手胜出。
#include
using namespace std;
int T;
int a[32];
int main()
{
cin>>T;
while(T--)
{
memset(a,0,sizeof(a));
int n;
cin>>n;
int ans=0;
for(int j=0;j>x;
ans^=x;
for(int i=1;i<=30&&x;i++)
{
if(x&1) a[i]++;
x>>=1;
}
}
if(!ans) cout<<0<0;i--)
{
if(a[i]==1)
{
cout<<1<
并查集
//answer :80
#include
using namespace std;
int mp[8][8],vis[8],fa[8],ans=0;
int find(int x)
{
if(fa[x]==x) return x;
else return fa[x]=find(fa[x]);
}
void dfs(int k)
{
if(k>7)
{
for(int i=1;i<=7;i++) fa[i]=i;
for(int i=1;i<=7;i++)
{
for(int j=1;j<=7;j++)
{
if(mp[i][j]&&vis[i]&&vis[j])
{
int x=find(i),y=find(j);
if(x!=y) fa[x]=y;
}
}
}
int cnt=0;
for(int i=1;i<=7;i++)
{
if(vis[i]&&fa[i]==i) cnt++;
}
if(cnt==1) ans++;
return;
}
vis[k]=1;
dfs(k+1);
vis[k]=0;
dfs(k+1);
}
int main()
{
mp[1][2]=mp[2][1]=1;
mp[1][6]=mp[6][1]=1;
mp[2][3]=mp[3][2]=1;
mp[2][7]=mp[7][2]=1;
mp[3][4]=mp[4][3]=1;
mp[3][7]=mp[7][3]=1;
mp[4][5]=mp[5][4]=1;
mp[5][6]=mp[6][5]=1;
mp[5][7]=mp[7][5]=1;
mp[6][7]=mp[7][6]=1;
dfs(1);
cout<
子串分值https://www.acwing.com/problem/content/2871/
#include
using namespace std;
const int N=1e5+5;
int pre[N],next[N],a[27];
string s;
long long ans=0;
int main()
{
cin>>s;
int n=s.size();
s=' '+s;//让下标从1开始
for(int i=1;i<=n;i++)
{
pre[i]=a[s[i]-'a'];
a[s[i]-'a']=i;
}//滚动一轮,得到pre数组
for(int i=0;i<26;i++)
a[i]=n+1;//为了让next有初始的值(最后)
for(int i=n;i>0;i--)
{
next[i]=a[s[i]-'a'];
a[s[i]-'a']=i;
}//滚动一轮,得到next数组
for(int i=1;i<=n;i++)
ans+=(i-pre[i])*(next[i]-i)
//每个字符(设为ch)有一个贡献的区间 (上次出现ch的位置,下次出现ch的位置)
//从区间( pre[i],i)内任选一点, 从区间( next[i],i)内任选一点,s[i]在这样的两点构成的区间内只出现一次
//对于s[i],符合条件的区间有 (i-pre[i])*(next[i]-i)个
cout<
01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000
answer:DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR
典型的·BFS,值得关注的就是这种记录路径的办法。
#include
using namespace std;
struct position
{
int x,y;
string path;
};
int maze[55][55];
int vis[55][55];
int dx[4]={1,0,0,-1};
int dy[4]={0,-1,1,0};
char direction[4]={'D','L','R','U'};
vectorvec;
queueq;
bool judge(position next)
{
if(!maze[next.x][next.y]&&next.x>=1&&next.x<=30&&next.y>=1&&next.y<=50&&!vis[next.x][next.y])
return true;
return false;
}
void bfs()
{
position start;
start.x=1,start.y=1;
q.push(start);
while(q.size())
{
position now=q.front();
q.pop();
if(now.x==30&&now.y==50)
{
cout<
外卖店优先级https://www.acwing.com/problem/content/1243/
直接暴力肯定超时,我想着写O(m)的做法,结果错了六发。。。(腹诽:还不如暴力骗80分)要注意的小细节非常多。
大体的思路就是先把题给的数据存到对应编号的vec里,然后遍历这n家外卖店的vec,对于每家店的vec:
1.先把所有数据排序!(题目给的订单不是按照时间顺序排列的)
sort(vec[i].begin(),vec[i].end());
2.第一个数据:除了加2个优先级之外啥也不干(在第一个订单出现之前优先级一直都是0)
3.从第二个数据(如果存在的话)一直到最后一个数据:每次先处理空档造成的优先级下降(从vec[i][j]到vec[i][j-1]有(vec[i][j]-vec[i][j-1]-1)个时间段),如果优先级下降到小于等于3,标记被清出优先缓存,又由于当前订单会使得优先级加2,如果优先级大于5了,标记进入优先缓存。
if(vec[i][j]-vec[i][j-1]-1>0) priority=max(0,priority-(vec[i][j]-vec[i][j-1]-1)); if(priority<=3) flag=0; priority+=2; if(priority>5) flag=1;
3.从最后一个数据到T个时刻过去:从最后一个数据到T之间还有(T-vec[i][vec[i].size()-1])个时间段,会使优先级下降,最后判断一下是否会下降到小于等于3即可
priority=max(0,priority-(T-vec[i][vec[i].size()-1])); if(priority>3&&flag) cnt++;
全部代码:
#include
using namespace std;
vectorvec[100001];
int N,M,T,cnt;
int main()
{
cin>>N>>M>>T;
while(M--)
{
int t,num;
scanf("%d%d",&t,&num);
vec[num].push_back(t);
}
for(int i=1;i<=N;i++)
{
if(vec[i].empty()) continue;
sort(vec[i].begin(),vec[i].end());
int priority=0,flag=0;
for(int j=0;j0)
priority=max(0,priority-(vec[i][j]-vec[i][j-1]-1));
if(priority<=3) flag=0;
priority+=2;
//cout<<" priority:"<5) flag=1;
}
priority=max(0,priority-(T-vec[i][vec[i].size()-1]));
//cout<<"last:"<3&&flag) cnt++;
}
cout<
修改数组https://www.acwing.com/problem/content/1244/
#include
using namespace std;
int N;
int a[100001],fa[2000000];
int find(int x)
{
if(fa[x]==x) return x;
else return fa[x]=find(fa[x]);
}
int main()
{
cin>>N;
for(int i=1;i<=2000000;i++) fa[i]=i;
for(int i=1;i<=N;i++)
{
scanf("%d",&a[i]);
if(find(a[i])==a[i])
fa[a[i]]++;
else
{
a[i]=find(a[i]);
fa[a[i]]++;
}
cout<
状态压缩+01背包
糖果https://www.acwing.com/problem/content/1245/
状态表示:dp[i][s]为从前i个糖果中选,选出来的糖果种类状态为s,所需最少糖果包数
阶段划分:类似于01背包问题中,从前i个物品中选
转移方程:划分依据:对于第i个物品选还是不选
dp[i][s]=min(dp[i-1][s],dp[i-1][s^(s&w[i])]+1);
// 不选 选
//(s&w[i])表示当前糖果袋中和之前已有的重合的部分
//s^(s&w[i])表示去掉重合的部分(未选之前的状态)
边界:dp初始化为0x3f3f3f3f ,dp[0][0];
目标:dp[n][(1< 时间复杂度:O(n * 2^m) 二维数组(会爆): 滚动数组: 一维数组: 【问题描述】 【问题描述】 在一个字符串中,如果取出若干个字符,将这些字符按照在字符串中的顺序排列后是单调递增的,则成为这个字符串中的一个单调递增子序列。 例如,在字符串 lanqiao 中,如果取出字符 n 和 q,则 nq 组成一个单调递增子序列。类似的单调递增子序列还有 lnq、i、ano 等等。 小蓝发现,有些子序列虽然位置不同,但是字符序列是一样的,例如取第二个字符和最后一个字符可以取到 ao,取最后两个字符也可以取到 ao。小蓝认为他们并没有本质不同。 对于一个字符串,小蓝想知道,本质不同的递增子序列有多少个? 例如,对于字符串 lanqiao,本质不同的递增子序列有 21 个。它们分别是 l、a、n、q、i、o、ln、an、lq、aq、nq、ai、lo、ao、no、io、lnq、anq、lno、ano、aio。 请问对于以下字符串(共 200 个小写英文字母,分四行显示): 【答案提交】 【问题描述】 小蓝还有一个 4 × 4 的方格盒子,用于存放玩具蛇,盒子的方格上依次标着字母 A 到 P 共 16 个字母。 小蓝可以折叠自己的玩具蛇放到盒子里面。他发现,有很多种方案可以将玩具蛇放进去。 下图给出了两种方案: 【答案提交】 时间限制: 1.0s 内存限制: 256.0MB 【问题描述】 一位同学答疑的过程如下: 首先进入办公室,编号为 i 的同学需要 si 毫秒的时间。 【输入格式】 【输出格式】 【样例输入】 【样例输出】 【样例说明】 【评测用例规模与约定】#include
#include
#include
2020国赛
B 含2天数
小蓝特别喜欢 2,今年是公元 2020 年,他特别高兴,因为每天日历上都可以看到 2。
如果日历中只显示年月日,请问从公元 1900 年 1 月 1 日到公元 9999 年 12月 31 日,一共有多少天日历上包含 2。即有多少天中年月日的数位中包含数字2。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。//answer:1994240
#include
C 本质上升序列
小蓝特别喜欢单调递增的事物。
tocyjkdzcieoiodfpbgcncsrjbhmugdnojjddhllnofawllbhf
iadgdcdjstemphmnjihecoapdjjrprrqnhgccevdarufmliqij
gihhfgdcmxvicfauachlifhafpdccfseflcdgjncadfclvfmad
vrnaaahahndsikzssoywakgnfjjaihtniptwoulxbaeqkqhfwl
本质不同的递增子序列有多少个?
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
E 玩具蛇
小蓝有一条玩具蛇,一共有 16 节,上面标着数字 1 至 16。每一节都是一个正方形的形状。相邻的两节可以成直线或者成 90 度角。
请帮小蓝计算一下,总共有多少种不同的方案。如果两个方案中,存在玩具蛇的某一节放在了盒子的不同格子里,则认为是不同的方案。
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。//answer:552
#include
H 答疑
有 n 位同学同时找老师答疑。每位同学都预先估计了自己答疑的时间。老师可以安排答疑的顺序,同学们要依次进入老师办公室答疑。
然后同学问问题老师解答,编号为 i 的同学需要 ai 毫秒的时间。
答疑完成后,同学很高兴,会在课程群里面发一条消息,需要的时间可 以忽略。
最后同学收拾东西离开办公室,需要 ei 毫秒的时间。一般需要 10 秒、20 秒或 30 秒,即 ei 取值为 10000,20000 或 30000。
一位同学离开办公室后,紧接着下一位同学就可以进入办公室了。
答疑从 0 时刻开始。老师想合理的安排答疑的顺序,使得同学们在课程群里面发消息的时刻之和最小。
输入第一行包含一个整数 n,表示同学的数量。
接下来 n 行,描述每位同学的时间。其中第 i 行包含三个整数 si, ai, ei,意义如上所述。
输出一个整数,表示同学们在课程群里面发消息的时刻之和最小是多少。
3
10000 10000 10000
20000 50000 20000
30000 20000 30000
280000
按照 1, 3, 2 的顺序答疑,发消息的时间分别是 20000, 80000, 180000。
对于 30% 的评测用例,1 ≤ n ≤ 20。
对于 60% 的评测用例,1 ≤ n ≤ 200。
对于所有评测用例,1 ≤ n ≤ 1000,1 ≤ si ≤ 60000,1 ≤ ai ≤ 1000000,ei ∈ {10000, 20000, 30000},即 ei 一定是 10000、20000、30000 之一。#include