zoukankan      html  css  js  c++  java
  • 天天快乐编程2020年OI集训队 训练6题解

    本次训练题目为字符串、广搜和树状数组。

    1.4858: 多项式输出

    NOIP2009普及组T1
    真*模拟题,可以考虑下所有情况,第一项没有加,可以是减,如果是最后一项(常数项,直接输出即可),如果绝对值不是1,要变为整数,1次直接输出x
    wch的代码

    #include<bits/stdc++.h>
    using namespace std;
    int a[105];
    int main()
    {
    	int n;
    	cin>>n;
    	for(int i=0;i<=n;i++)
    	{
    		cin>>a[i];
    		if(a[i]==0) continue;
    		if(a[i]>0 && i!=0)
    			cout<<"+";
    		if(a[i]<0)
    			cout<<"-";
    		if(i==n)
    		{
    			cout<<abs(a[i]);
    			break;
    		}
    		if(abs(a[i])!=1)
    			cout<<abs(a[i]);
    		if(i==n-1)
    			cout<<"x";
    		if(a[i]!=0 && i!=n-1)
    		{
    			cout<<"x^"<<n-i;
    		}
    	}
    	return 0;
    }
    

    2.3328: isbn

    NOIP2008普及组T1
    又是一个模拟题,由于有个X,我们可以直接把它变成10,然后注意下过程的相关数据即可。

    #include <bits/stdc++.h>
    using namespace std;
    char dp[1005];
    int a[6]={1,2,3,5,10,20},b[6];
    int main()
    {
    	//ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    	int a,b,c,e;
    	char d;
    	while(cin>>a>>d>>b>>d>>c>>d>>d)
    	{
    		if(a==0&&b==0&&c==0&&d=='0')break;
    		if(d=='X')e=10;
    		else e=d-'0';
    		int ans=0,cs=9,t;
    		t=c;
    		for(int i=0;i<5;i++)
    		{
    			ans=(ans+cs*(t%10))%11;
    			t/=10,cs--;
    		}
    		t=b;
    		for(int i=0;i<3;i++)
    		{
    			ans=(ans+cs*(t%10))%11;
    			t/=10,cs--;
    		}
    		ans=(ans+a)%11;
    		if(ans==e)
    		cout<<"Right
    ";
    		else 
    		{
    			if(ans==10)
    			printf("%d-%03d-%05d-X
    ",a,b,c);
    			else printf("%d-%03d-%05d-%d
    ",a,b,c,ans);
    		}
    	}
    	return 0;
    }
    

    3.4849: Jam的计数法

    NOIP2006普及组T3
    这道题其实是输出一个字符串(可以把a看成1,b看成2,以此类推……)满足进制的后面连续5个字符串,这个进制是t+1进制(逢t进一),最小数是s(s没什么用)。不过有一个很奇怪的规定,就是后面的数一定要比前面的数大。

    拿样例来看:bdfij

    首先最后一位j已经不能再+1了,那就向前一位i进一,进一后变成j,后面的数要比前面的数大,所以j还要再向f进一,f进一变成g,满足了后面两个数都可以比前一个数大的条件,这时要求和原数只差1的数,倒数第二位就变成g+1=h,最后一位就变成h+1=i,结果就是bdghi。

    实现的话就是先把最后一位+1,看有没有超过进制,如果没有,就直接输出,继续下一个+1,如果有,就把前面一位+1,再看有没有超出进制,没有的话把后面的数(最后一位)依次从前往后赋值成前一个数(字符)+1,输出,有的话重复上面的步骤,如果第一位进一满足不了后面的数比前面的数大的条件的话,就终止程序,否则输出5次就行了。

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        int a,b,n,i2=0;
        string s;
        cin>>a>>b>>n>>s;
        for(int i=1;i<=5;i++)
        {
            s[n-1]++;
            for(int q=n-1;;q--)
            {
                if(s[q]>=b+'a'-i2)
                {
                    s[q-1]++;
                    if(q==0) return 0;
                }
                else
                {
                    for(int j=q+1;j<=n-1;j++)
                    {
                        s[j]=s[j-1]+1;
                    }
                    break;
                }
                i2++;
            }
            cout<<s<<endl;
            i2=0;
        }
        return 0;
    }
    

    4.4829: 计算器的改良

    NOIP2000普及组T1
    这个题目需要把带未知数的移动到一边,数字移动到另一边,然后进行求解,需要注意细节很多
    我的比较短的代码

    #include<stdio.h>
    char s[105],c;
    int main()
    {
        scanf("%s",s);
        int x=0,n=0,f=1,k=1;
        for(int i=0; s[i]; i++)
        {
            if(s[i]=='=')  f=-1,k=1;
            else if(s[i]=='-')k=-1;
            else if(s[i]=='+')k=1;
            else
            {
                int a=0;
                while(s[i]>='0'&&s[i]<='9')a=a*10+s[i++]-'0';
                if(s[i]>='a'&&s[i]<='z')
                    c=s[i],x-=f*k*(a==0?1:a);
                else
                    n+=f*k*a,i--;
            }
        }
        printf("%c=%.3f",c,n*1.0/x);
    }
    
    

    5.4890: 寻找道路

    NOIP2014提高组Day2T2
    这是图论的题目,可以用BFS解决。
    首先,预处理,把每条边反向。
    从终点开始bfs,标记从终点开始可以走到的点。
    第二步,枚举每一个点,如果这个点没有被标记,则枚举它的每一条出边(反向后的),如果它指向的点被标记,则说明这个被标记的点不合法,删除。
    第三步,在合法点上bfs,单源最短路。

    
    #include<bits/stdc++.h>
    using namespace std;
    const int N = 10005;
    vector<int> G[N];
    bool v1[N], v2[N];
    queue<int> q;
    int s, t, ans = 0;
    bool a = false;
    struct T
    {
    	int pos, step;
    };
    void bfs1()
    {
    	q.push(t);
    	v1[t] = true;
    	while (!q.empty())
    	{
    		int x = q.front();
    		q.pop();
    		int size = G[x].size();
    		for (int i = 0; i < size; i++)
    		{
    			if (!v1[G[x][i]])
    			{
    				v1[G[x][i]] = true;
    				q.push(G[x][i]);
    			}
    		}
    	}
    }
    
    void bfs2()
    {
    	queue<T> q;
    	T now, next;
    	now.pos = t;
    	now.step = 0;
    	q.push(now);
    	while (!q.empty())
    	{
    		T x = q.front();
    		q.pop();
    		now = x;
    		if (now.pos == s)
    		{
    			a = true;
    			ans = now.step;
    			return;
    		}
    		for (int i = 0; i < G[now.pos].size(); i++)
    		{
    			if (!v2[G[now.pos][i]])
    			{
    				next.pos = G[now.pos][i];
    				next.step = now.step + 1;
    				q.push(next);
    			}
    		}
    	}
    }
    int main()
    {
    	memset(v1, false, sizeof(v1));
    	memset(v2, false, sizeof(v2));
    	int v, l;
    	cin >> v >> l;
    	for (int i = 1; i <= l; i++)
    	{ 
    		//反向建图
    		int x, y;
    		cin >> x >> y;
    		G[y].push_back(x);
    	}
    	cin >> s >> t;
    	// bfs 找出没有访问到的节点,然后将该结点前一个节点设置为已访问过
    	//这样第二次 bfs 就不会访问这个节点
    	bfs1();
    	for (int i = 1; i <= v; i++)
    	{
    		if (!v1[i])
    		{
    			for (int j = 0; j < G[i].size(); j++)
    				v2[G[i][j]] = true;
    		}
    	}
    	bfs2();
    	if (a)
    		cout << ans << endl;
    	else
    		cout << "-1
    ";
    	return 0;
    }
    

    6.4816: 引水入城

    NOIP2010提高组T4
    需要使用BFS或DFS和动态规划,纯粹的BFS在提高组出现不多。
    第一问很好做,直接BFS求一下最下面一排的店有没有不能被覆盖到的就行了
    第二问明显的思路是对第一排每个点进行dfs或bfs,搜出每个点能够覆盖到的区间,再做线段覆盖就行了,这样复杂度很高
    我们可以不可以剪枝+贪心覆盖呢,我竟然过了

    #include <bits/stdc++.h>
    using namespace std;
    const int INF = 0x7fffffff, N = 505;
    struct T
    {
    	int x, y;
    };
    struct E
    {
    	int l, r;
    } edge[N];
    
    int n, m, a[N][N];
    queue<T> q;
    int dir[4][2] = {0, -1, 0, 1, -1, 0, 1, 0};
    int vis[N][N];
    int lm, rm;
    int size = 0;
    int cmp(E a, E b)
    {
    	if (a.l == b.l)
    		return a.r < b.r;
    	return a.l < b.l;
    }
    int check(T nex, T now)
    {
    	int x = nex.x, y = nex.y;
    	int nx = now.x, ny = now.y;
    	if (x < 1 || y < 1 || x > n || y > m || a[nx][ny] <= a[x][y])
    		return 0;
    	return 1;
    }
    void bfs(T st, int o)
    {
    	while (!q.empty())
    		q.pop();
    	T now, nex;
    	q.push(st);
    	vis[1][o] = o; //这里很巧妙,避免了每次都要重新设为零。
    	while (!q.empty())
    	{
    		now = q.front();
    		q.pop();
    		for (int i = 0; i < 4; i++)
    		{
    			nex.x = now.x + dir[i][0];
    			nex.y = now.y + dir[i][1];
    			if (vis[nex.x][nex.y] == o)
    				continue;
    			if (check(nex, now))
    			{
    				vis[nex.x][nex.y] = o;
    				q.push(nex);
    				if (nex.x == n)
    				{
    					//到达最后一行
    					lm = min(lm, nex.y);
    					rm = max(rm, nex.y);
    				}
    			}
    		}
    	}
    	edge[o].l = lm;
    	edge[o].r = rm;
    }
    int main()
    {
    	cin >> n >> m;
    	for (int i = 1; i <= n; i++)
    		for (int j = 1; j <= m; j++)
    			cin >> a[i][j];
    	T fir;
    	for (int i = 1; i <= m; i++)
    	{
    		if (a[1][i] >= a[1][i - 1] && a[1][i] >= a[1][i + 1]) //重要剪枝,如果这个比两边都大或者相等才bfs,否则见else,将该边左端点设为INF,都端点设为0。
    		{
    			lm = INF;
    			rm = 0;
    			fir.x = 1;
    			fir.y = i;
    			bfs(fir, i);
    		}
    		else
    		{
    			edge[i].l = INF;
    			edge[i].r = 0;
    		}
    	}
    	int cnt = 0;
    	for (int i = 1; i <= m; i++)
    		if (vis[n][i] == 0)
    			cnt++;
    	if (cnt)
    	{
    		cout << 0 << endl
    			 << cnt;
    		return 0;
    	}
    	cout << 1 << endl;
    	sort(edge + 1, edge + m + 1, cmp);
    	edge[1].l = 1;
    	int now = 0, to = 0, ans = 0;
    	for (int i = 1; i <= m; i++)
    	{
    		if (edge[i].l == INF)
    			continue;
    		if (now + 1 >= edge[i].l)
    			to = max(edge[i].r, to);
    		else
    		{
    			ans++;
    			now = to;
    			to = max(to, edge[i].r);
    		}
    	}
    	if (now != m)
    		ans++;
    	cout << ans;
    	return 0;
    }
    

    7.5998: 列队

    NOIP2017提高组 DAY2 T3
    需要使用树状数组或线段树进行求解。
    定义第i行为所有的点(i,j),0<j<m
    可以发现,每一行是相对独立的,每一次操作只会影响到当前行和最后一列
    考虑每一行和最后一列各开一个树状数组,但这样显然会爆空间
    实际上,对于没有离队过的点是没必要储存的,可以直接算出编号,
    因此只要用vector储存每一行和最后一列后加入的点即可
    还需要预处理一个数组d[i]表示第i次询问当前行的离队的点的纵坐标
    这个可以离线做出来,然后只需要对最后一列维护一个树状数组即可

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 300005;
    #define lowbit(x) ((x) & (-x))
    struct T
    {
        int p, id;
    } A[N];
    int n, m, qn, mx, d[N], a[N << 1], q[N][2];
    vector<T> t[N];
    vector<ll> v[N], lst;
    void add(int x, int y)
    {
        for (; x <= mx; x += lowbit(x))
            a[x] += y;
    }
    int Q(int x)
    {
        int r = 0;
        for (; x; x -= lowbit(x))
            r += a[x];
        return r;
    }
    
    int Find(int R)
    {
        int tmp = 0;
        for (int l = 0, r = mx; l <= r;)
        {
            int mid = (l + r) >> 1;
            if (Q(mid) >= R)
                tmp = mid, r = mid - 1;
            else
                l = mid + 1;
        }
        return tmp;
    }
    int main()
    {
        cin >> n >> m >> qn;
        mx = max(n, m) + qn;
        for (int i = 1; i <= qn; i++)
        {
            cin >> q[i][0] >> q[i][1];
            if (q[i][1] != m)
                t[q[i][0]].push_back({q[i][1], i});
        }
        for (int i = 1; i <= mx; i++)
            add(i, 1);
        for (int i = 1; i <= n; i++)
        {
            for (int j = 0; j < t[i].size(); j++)
                add(d[t[i][j].id] = Find(t[i][j].p), -1);
            for (int j = 0; j < t[i].size(); j++)
                add(d[t[i][j].id], 1);
        }
        ll Ans;
        for (int i = 1; i <= qn; i++)
        {
            int x = Find(q[i][0]);
            Ans = (x <= n) ? (ll)x * m : lst[x - n - 1];
            add(x, -1);
            if (q[i][1] != m)
            {
                v[q[i][0]].push_back(Ans);
                Ans = (d[i] < m) ? (q[i][0] - 1) * 1ll * m + d[i] : v[q[i][0]][d[i] - m];
            }
            lst.push_back(Ans);
            printf("%lld
    ", Ans);
        }
    }
    
  • 相关阅读:
    Leetcode
    算法
    手写代码注意点 -- HashMap
    Batch
    Batch
    Tomcat
    微服务
    Java 基础
    Maven
    算法
  • 原文地址:https://www.cnblogs.com/BobHuang/p/13765594.html
Copyright © 2011-2022 走看看