洛谷题单(上午)

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;
}

最大公约数构造不递减序列(没改好)

CF1980E

点击查看代码
#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 在线和离线(算法)

在线算法:是指它可以以序列化的方式一个个的处理输入,也就是说在开始时并不需要已经知道所有的输入
离线算法:在开始时就需要知道问题的所有输入数据,而且在解决一个问题后就要立即输出结果。

posted on 2024-07-29 09:24  Hoshino1  阅读(1)  评论(0编辑  收藏  举报