洛谷题单(上午)
https://www.luogu.com.cn/training/9349
字符串读入
getline(cin,a);
//读入一行包括空格
for(int i=0;i<a.size();i++)
{
if(a[i]!=' '&&a[i]!='\n')
ans++;
}
打表和ascl运用
点击查看代码
#include <stdio.h>
int main(void){
char a[14], mod[12] = "0123456789X"; //先将mod11后的十一个字符存入数组
gets(a); //输入字符串
int i, j = 1, t = 0;
for(i = 0; i < 12; i++) {
if(a[i] == '-') continue; //字符串为分隔符‘-’时跳过此次循环进入下一次循环
t += (a[i]-'0')*j++; //t储存 第j个 数字 * j 的和
}
if(mod[t%11] == a[12]) printf("Right");
else {
a[12] = mod[t%11]; //若识别码错误,则赋正确的识别码,然后输出
puts(a);
}
return 0;
}
统计单词个数
重点
1.getline(cin,a)
2.tolower(a[i]);
3. a=' '+a+' ';(只统计独立单词的思维转化)
4.b.find(a)!=string::npos(find的运用)
点击查看代码
#include <iostream>
#include <string>
//命名空间
using namespace std;
int main(){
//定义两个字符串
string a;
string b;
//用string库,调用getline, 直接读入一整行
getline(cin,a);
getline(cin,b);
//转换大小写,可以都转换为大写,或者小写
for (int i=0;i<a.length();++i){
a[i]=tolower(a[i]);
}
for (int i=0;i<b.length();++i){
b[i]=tolower(b[i]);
}
//因为连起来的不算,所以要在前后加几个空格,一定要是同样多的,同量减同量,等于同量
a=' '+a+' ';
b=' '+b+' ';
//先看看会不会找不到,用a.find()和string::npos
if (b.find(a)==string::npos){
cout<<-1<<endl;
}
//如果找得到
else {
int alpha=b.find(a);
int beta=b.find(a),s=0;//计数器初始化为0
while (beta!=string::npos){
++s;//计数器
beta=b.find(a,beta+1);
}
cout<<s<<" "<<alpha<<endl;//输出第一个和总共有几个
}
//函数返回值为0,结束整个程序
return 0;
}
判断两个日期间回文串的个数
重点
1.月份打表
2.思维转化,枚举月日。再看是否合理
3.朴素算法: 依次枚举,日满月满归一
技巧
#inlcude<bits/stdc++.h>
using namespace std;
int i,j,n,m,a,b,c,sum,ans;
int s[13]={0,31,29,31,30,31,30,31,31,30,31,30,31};
int main()
{
scanf("%d%d",&n,&m);
for (i=1;i<=12;i++)//枚举月和日
for (j=1;j<=s[i];j++)
{
c=(j%10)*1000+
(j/10)*100+
(i%10)*10+
(i/10);//算出前四位。
sum=c*10000+i*100+j;//算出整个日期
if (sum<n||sum>m) continue;
ans++;//统计
}
printf("%d",ans);
return 0;
}
朴素算法
#include <bits/stdc++.h>
using namespace std;
int y1,m1,d1,y2,m2,d2,sum;
int day[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};//存每个月的天数(2月是没用的)
bool check()
{
return (y1<y2)||((y1==y2&&m1<m2)||(y1==y2&&m1==m2&&d1<=d2));
//首先,只要现在的年份小于结束年份就一定可以继续循环;然后,如果现在的年份和结束年份相等,且月份小于结束年份也可以循环;最后,只有天数不同,就必须现在的天数小于结束天数。
}
int r(int x)
{
return ((x%4==0&&x%100!=0)||(x%400==0))?29:28;//判断2月的天数
}
bool palindrome(int y,int m,int d)
{
char s[8];
int t=y*10000+m*100+d;//将它重新组成日期
sprintf(s,"%d",t);//转换为字符
//可以替换为string s=to_string(t);
for(int i=0;i<4;i++)
if(s[i]!=s[8-i-1])
return false;
return true;//判断回文
}
int main()
{
int t1,t2;
cin>>t1>>t2;
y1=t1/10000; m1=t1/100%100; d1=t1%100;//分离出年份月份和日数
y2=t2/10000; m2=t2/100%100; d2=t2%100;
while(check())//判断是否枚举完
{
bool f=1;//f用于看是否一已经进行过日期更新
if(palindrome(y1,m1,d1))//原谅我用这么长的单词装逼
sum++;
if((m1==2&&d1==r(y1))||(m1!=2&&d1==day[m1]))//一个月完了
{
m1++;
d1=1;
f=0;
}
if(m1==13)//一年完了
{
y1++;
m1=1;
d1=1;
f=0;
}
if(f)//如果月没有结束,就直接加天数即可
d1++;
}
cout<<sum<<endl;
return 0;
}
贪心的邻项比较法
拼数问题
bool cmp(const string &a,const string &b) { // &表示引用
return (a+b > b+a);
}
正确字符个数
https://www.luogu.com.cn/problem/P5587
重点
1.pop_back()和push_back是string在最后删去一个字符或加上一个字符
2.如何读入 while(getline(cin,s1),s1!="EOF")
3.kpm=rig*60.0/t+0.5;实现四舍五入
4.有的要开longlong
stl法
#include<bits/stdc++.h>
#pragma GCC optimize(3)
using namespace std;
const int N=1e4+5;
string s[N],t[N],s1;
long long n,m,cnt;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
while(getline(cin,s1),s1!="EOF")
{
n++;//读入字符串
for(char i:s1)
{
if(i=='<')
{
if(!s[n].empty())s[n].pop_back(); //模拟,如果是退格且string中还有字符就删掉一个
}
else s[n].push_back(i);//否则加上去
}
}
while(getline(cin,s1),s1!="EOF")
{
if(++m>n)break;
for(char i:s1)
{
if(i=='<')
{
if(!t[m].empty())t[m].pop_back();
}
else t[m].push_back(i);//同上
}
for(int i=0; i<min(t[m].size(),s[m].size()); i++)cnt+=s[m][i]==t[m][i]; //逐位比较
}
cin>>m;
cout<<(long long)(cnt*60.0/m+0.5);//输出
}
栈
#include<bits/stdc++.h>
using namespace std;
long long hs,es,cds,rig,kpm;
double t;
char s[100005];
stack<char>s1[10005],s2;
int main(){
while(1){
gets(s);
cds=strlen(s);
if(cds==3&&s[0]=='E'&&s[1]=='O'&&s[2]=='F')break;
//还可以这样判断结束
hs++;
for(int i=0;i<cds;i++){
if(s[i]=='<'&&s1[hs].empty()==0)s1[hs].pop();
else if((s[i]>='a'&&s[i]<='z')||s[i]==' '||s[i]=='.')s1[hs].push(s[i]);
}
}
while(1){
gets(s);
cds=strlen(s);
if(cds==3&&s[0]=='E'&&s[1]=='O'&&s[2]=='F')break;
es++;
for(int i=0;i<cds;i++)
{
if(s[i]=='<'&&s2.empty()==0)s2.pop();
else if((s[i]>='a'&&s[i]<='z')||s[i]==' '||s[i]=='.')s2.push(s[i]);
}
stack<char>t1,t2;
//临时栈为了顺序比较(不然是从字符串尾比较到头部)
while(s1[es].empty()==0){
t1.push(s1[es].top());
s1[es].pop();
}
while(s2.empty()==0){
t2.push(s2.top());
s2.pop();
}
cds=min(t1.size(),t2.size());
for(int i=1;i<=cds;i++){
if(t1.top()==t2.top())rig++;
t1.pop();
t2.pop();
}
}
cin>>t;
kpm=rig*60.0/t+0.5;
//题目要求四舍五入所以+0.5
cout<<kpm<<endl;
return 0;
}
数的划分
重点
1.剪枝
if(sum+i*(k-x+1)>n)
return ;
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=210;
int arr[maxn];
int n,k;
int ans=0;
void dfs(int x,int start,int sum)
{
if(sum>n)
return ;
if(x>k)
{
if(sum==n)
ans++;
return ;
}
for(int i=start; i<n; i++)
{
if(sum+i*(k-x+1)>n)
return ;
arr[x]=i;
dfs(x+1,i,sum+i);
arr[x]=0;
}
}
signed main()
{
cin>>n>>k;
dfs(1,1,0);
cout<<ans;
return 0;
}
cf div3 950补题(下午)
https://codeforces.com/contest/1980
集合去重,集合遍历
重点
1.cin后如果要用getline一定要getchar
2.集合遍历
3.不同样例之间,全局变量一定要清空
map.clear();
for(char c : se)
{
cout << c << " ";
}
//第二种
for(auto it = se.begin(); it != se.end(); ++it)
{
cout << *it << " ";
}
点击查看代码
#include<bits/stdc++.h>
using namespace std;
void solve()
{
// int n,m;
// cin>>n>>m;
string a;
getline(cin,a);
set<char> se;
for(int i=0;i<a.length();i++)
se.insert(a[i]);
cout<<se.size();
for(auto it=se.begin();it!=se.end();it++)
cout<<*it<<endl;
cout<<(int)7-se.size()<<endl;
}
signed main()
{
int t;
cin>>t;
getchar();
while(t--)
solve();
return 0;
}
c题 map
https://www.luogu.com.cn/problem/solution/CF1980C
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fst ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
const int N = 1e6 + 10;
int n, m, a[N], b[N], x[N];
signed main()
{
fst;
int t;
cin >> t;
while(t--)
{
cin >> n;
for(int i=1; i<=n; i++)
{
cin >> a[i];
}
//要把a数组通过d数组改成b数组
map<int, int> mp, Mp, v;
for(int i=1; i<=n; i++)
{
cin >> b[i];
if(a[i] != b[i]) mp[b[i]]++;
else v[a[i]] = 1;
//这个字母起初是否合法
}
cin >> m;
for(int i=1; i<=m; i++)
{
cin >> x[i];
Mp[x[i]]++;
//输入d数组,统计工具数字数量
}
bool f = 1;
for(int i=1; i<=n; i++)
{
if(Mp[b[i]] < mp[b[i]])
{
//工具数字数量
// 需要修改的数量
f = 0;
break;
}
}
// 数量上够,最开始合法或者要修改的才能在最后
if(f!=0&&(mp[x[m]] ||v[x[m]]) ) puts("YES");
//v[m]是工具数最后一个数,这个数要么是要改的数要么他是合法的数(是目标序列数)
else puts("NO");
}
return 0;
}
最大公约数构造不递减序列(没改好)
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fst ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
const int maxn=2e5+10;
int a[maxn],b[maxn];
void solve()
{
memset(a,0,sizeof a);
memset(b,0,sizeof b);
int n;
int j=1;
int cnt=0;
cin>>n;
cin>>a[1];
for(int i=2; i<=n; i++)
{
cin>>a[i];
b[j++]=__gcd(a[i-1],a[i]);
}
j--;
for(int i=1;i<=j;i++)
cout<<b[i]<<" ";
cout<<endl;
cout<<"*";
for(int i=1; i<j; i++)
{
b[i]=b[i+1]-b[i];
cout<<b[i]<<" ";
}
cout<<endl;
int flag=1;
for(int i=1; i<j; i++)
{
if(b[i]<0)
{
cnt++;
if(b[i]+b[i+1]<0||b[i]+b[i-1]<0)
{
flag=0;
}
}
}
cout<<cnt<<" "<<flag<<endl;
if(flag==0)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
signed main()
{
int t;
cin>>t;
while(t--)
solve();
return 0;
}
vj团队5补题(晚上)
https://vjudge.net/contest/643995
1 字符串的模拟
字符串相关补充
1.csdn
2.substr(pos,len),从pos开始第len个位置
string s="abcdefg";
//s.substr(pos1,n)返回字符串位置为pos1后面的n个字符组成的串
string s2=s.substr(1,5);//bcdef
自己写的
#include <bits/stdc++.h>
using namespace std;
int main()
{
map<string, char> mp = {
{"iu", 'q'}, {"ei", 'w'}, {"uan", 'r'}, {"ue", 't'}, {"un", 'y'}, {"sh", 'u'}, {"ch", 'i'}, {"uo", 'o'}, {"ie", 'p'}, {"ong", 's'}, {"iong", 's'}, {"ai", 'd'}, {"en", 'f'}, {"eng", 'g'}, {"ang", 'h'}, {"an", 'j'}, {"uai", 'k'}, {"ing", 'k'}, {"uang", 'l'}, {"iang", 'l'}, {"ou", 'z'}, {"ia", 'x'}, {"ua", 'x'}, {"ao", 'c'}, {"zh", 'v'}, {"ui", 'v'}, {"in", 'b'}, {"iao", 'n'}, {"ian", 'm'},{"q", 'q'}, {"w", 'w'}, {"e", 'e'}, {"r", 'r'}, {"t", 't'},
{"y", 'y'}, {"u", 'u'}, {"i", 'i'}, {"o", 'o'}, {"p", 'p'},
{"a", 'a'}, {"s", 's'}, {"d", 'd'}, {"f", 'f'}, {"g", 'g'},
{"h", 'h'}, {"j", 'j'}, {"k", 'k'}, {"l", 'l'}, {"z", 'z'},
{"x", 'x'}, {"c", 'c'}, {"v", 'v'}, {"b", 'b'}, {"n", 'n'},
{"m", 'm'}};
string s;
while (getline(cin, s))
{
//注意读入
string s1;
for (int i = 0; i < s.length(); i++)
{
//判断是否为本行最后一个,因为最后一个找不到空格
if (s[i] != ' '&&1+i!=s.length())
{
s1 += s[i];
}
else
{
if(1+i==s.length())
s1 += s[i];
if (s1.size() == 1 || s1.size() == 2)
{
if(s1.size() == 1)
cout<<s1;
cout << s1 << " ";
}
else
{
if ((s1[0] == 's' || s1[0] == 'c' || s1[0] == 'z') && s1.size() >= 3 && s1[1] == 'h')
{
string s2 = s1.substr(0, 2);
cout << mp[s2];
string s3 = s1.substr(2);
//cout<<s1<<" "<<s2<<" "<<s3<<"111111111"<<mp[s3];
cout << mp[s3];
}
else
{
if(s1=="ang")
cout<<"ah";
else
{
cout << s1[0];
string s2 = s1.substr(1);
cout << mp[s2];
}
}
cout << " ";
}
s1 = ""; // 重置 s1,准备处理下一个单词
//记得输出空格,重置变量,输出换行符
}
}
cout << endl;
}
return 0;
}
2 if语句之间的判断顺序致错
if(sum>=120)cout<<sum-50<<endl;
else if(sum>=89)cout<<sum-30<<endl;
else if(sum>=69)cout<<sum-15<<endl;
//这条放到前面就需要写成sum>89&&sum<120
3 数学思维的转化
找半完美数直接找6倍即可,输入一个p,找k是p的倍数,使得k是半完美数
6=1+2+3所以6n=1+2n+3n;
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
const int N = 1e6 + 10;
const int M = 1e3 + 5;
const int inf = 0x3f3f3f3f;
void solve()
{
int p;
cin>>p;
cout<<p*6<<" "<<3<<endl;
cout<<p<<" "<<p*2<<" "<<p*3<<endl;
}
signed main()
{
int t=1;
cin>>t;
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
while (t--)
{
solve();
}
return 0;
}
4 并查集
原题:卢西达占据了由m条无向道路连接的n个城市,每条道路都有一个强度ki。敌人将进攻摧毁这些道路。当敌人发动伤害为x的攻击时,所有强度小于x的道路都将被摧毁。
现在露西达有Q个问题要问你,如果敌人以伤害pi发动攻击,有多少对城市彼此可以到达。城市x和城市y是可到达的,这意味着有一条从x到y的路径,该路径中每条道路的强度大于或等于pi。
重点
1.思维转化看成并查集问题
2.重载结构体:
struct node
{
int x, y, k;
bool operator < (const node &rd)const //按路的权值降序(先搭能搭的)
{
return k > rd.k;
}
} road[N];
题解1
题解二:
存储:用结构体存道路的起始和权值,以及询问的伤害和序号。重载分别按权值和伤害降序排序。
算法:并查集,维护集合大小。
操作:将道路按权值降序排序(为了先连接可连接的城市),询问按伤害降序排序(伤害小的集合大小可以在伤害大的集合基础上累加)。对于每一个伤害值,累加新连通的两个集合的城市数,将可合并且未合并的集合合并,并更新集合大小。
题解二
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
int p[N]; //记祖先和集合的大小
ll size[N], ans[N];
struct node
{
int x, y, k;
bool operator < (const node &rd)const //按路的权值降序(先搭能搭的)
{
return k > rd.k;
}
} road[N];
struct query
{
int id, p;
bool operator < (const query &qy)const //按伤害的值降序(先搭条件严苛的)
{
return p > qy.p;
}
} q[N];
int find(int x) //并查集
{
return x == p[x] ? x : find(p[x]);
}
int main()
{
int T;
cin >> T;
while (T --)
{
int n, m, Q;
cin >> n >> m >> Q;
memset(ans, 0, sizeof ans);
//每次询问记得初始化为0,这是全局变量
for (int i = 1; i <= n ; ++ i) //初始化
{
p[i] = i;
size[i] = 1;
}
//并查集分种类,这个还需要维护集合大小
for (int i = 0; i < m; ++ i)
{
int x, y, k;
cin >> x >> y >> k;
road[i] = {x, y, k};
//带权值的好像经常用结构体,结构体存路,不带权值习惯vector?
}
for (int i = 0; i < Q; ++ i)
{
cin >> q[i].p;
q[i].id = i;
}
sort(road, road + m); //结构体排序,一定要重载小于号不然会报错
sort(q, q + Q);
ll sum = 0;//注意啥时候需要用longlong ,因为集合数量可能很多(点多)
int j = 0;
for (int i = 0; i < Q; ++ i)
{
int damage = q[i].p;
while (j < m && road[j].k >= damage) //能搭且还有点路没有遍历
{
int u = find(road[j].x);
int v = find(road[j].y);
if (u != v) //未搭
{
sum += (ll)size[u] * size[v]; //为两个集合的城市搭桥,注意要强制转化为longlong
p[u] = v; //合并顶点
size[v] += size[u]; //合并大小
}
++ j;
}
ans[q[i].id] = sum;
}
for (int i = 0; i < Q; ++ i) cout << ans[i] << endl;
}
return 0;
}
/*因为我们是在不断向集合中加入元素,所以强度和伤害都按降序排序,都先处理大的,这样才能保证第一次第二次..*/
/*前面的询问不影响后面的询问,因为如果伤害大时能加入的路线在伤害小时一定能加入。*/
自己写的
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1e6+10;
long long sizes[maxn];
long long ans[maxn];
int p[maxn];
struct node
{
int x;
int y;
int k;
bool operator<(const node &r1)const
{
return k>r1.k;
}
} road[maxn];
struct query
{
int p;
int id;
bool operator<(const query &q1)const
{
return p>q1.p;
}
} q[maxn];
int find(int x)
{
return x==p[x]?x:p[x]=find(p[x]);
//路径压缩,这里写错了 p[x]=find(p[x])
//而不是x=find(p[x])
}
void solve()
{
int n,m,q1;
cin>>n>>m>>q1;
memset(ans,0,sizeof ans);//忘记重置
for(int i=1; i<=n; i++)
{
p[i]=i;
sizes[i]=1;
}
for(int i=1; i<=m;i++)
{
cin>>road[i].x>>road[i].y>>road[i].k;
}
for(int i=1; i<=q1; i++)
{
cin>>q[i].p;
q[i].id=i;
}
sort(road+1,road+m+1);
sort(q+1,q+q1+1);
int sum=0;
int j=1;
for(int i=1; i<=q1; i++)
{
while(j<=m&&road[j].k>=q[i].p)
{
// int u=find()road[j].x;
// int v=road[j].y;
// if(find(u)!=find(v))
// {
// sum+=(long long)sizes[find(u)]*sizes[find(v)];
// p[find(u)]=find(v);
// sizes[find(v)]+=sizes[find(u)];
//
// }
int u=find(road[j].x);
int v=find(road[j].y);
if(u!=v)
{
sum+=sizes[u]*sizes[v];
p[u]=v;
sizes[v]+=sizes[u];
//要分清谁是谁的父母,那个合并到哪个不然会超时
//这里要找到父母,都要写成find不然会导致错误
//这里不能像上面一样写,因为相当于你想把点的集合加进来,不是把合成后的加进来
}
j++;
}
ans[q[i].id]=sum;
}
for(int i=1; i<=q1; i++)
cout<<ans[i]<<endl;
}
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin>>t;
while(t--)
solve();
return 0;
}
5 在线和离线(算法)
在线算法:是指它可以以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入
离线算法:在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果。