第二期第6周周报

这周难度大幅度增加

第一题 漂亮数

    这题是构造,首先找到一个基数,让他循环,不难理解,整个数都是由这个基数循环构成。基数就是数值前面的三位。然后构造出来的数如果太小了就在基数上面循环+1就可以了。当成字符串处理比较好,熟练掌握字符串处理函数。在等长的情况下可以用字符串比较代替数字比较。

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;

void solve()
{
    int len,k;
    string ans,back,a;
    cin>>len>>k>>a;
    back=a;
    cout<<len<<endl;
    back.erase(back.begin()+k,back.end());
    while(ans.size()<a.size()) ans+=back;
    ans.erase(ans.begin()+len,ans.end());
    if(ans>=a)
	{
        cout<<ans;
    }
    else
	{
        ans.clear();
        int idx=back.size()-1;
        while(back[idx]=='9') back[idx]='0',idx--;
        back[idx]++;
        while(ans.size()<a.size()) ans+=back;
        ans.erase(ans.begin()+len,ans.end());
        cout<<ans;
    }
}
signed main()
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    solve();
    return 0;
}

第二题 真假字符串

    本来以为这题又是很难的dp,但是事实上就是搜索。题目的意思可以转化为S1和S2各删除一个字符可以得到一样的结果。需要分情况搜索。当遇到不匹配的情况就要分情况搜索,是删除哪个字符。但是要是遇到多个字符不相同的话就不行了。虽然不涉及递归但是和dfs的基本思路是一样的。

#include <bits/stdc++.h>
using namespace std;
//参数分别为的原串和另一个删除后的串
//s1是长串,s2是短串
bool dfs(string s1, string s2)
{
    int n = s1.size(),l=0,r=0;
    bool st = true;
    while (l < n && r < n)
    {
        if (s1[l] != s2[r])
        {
            if (st)
            {
                l++;
                st = false;
            }
            else return false;
        }
        else l++, r++;
    }
    if (l < n)return false;
    return true;
}

int main()
{
    string s1, s2;
    cin >> s1 >> s2;
    int n = s1.size(), ans = -1;
    for (int i = 0; i < n; i++)
    {
        if (s1[i] != s2[i])
        {
            ans = i;
            break;
        }
    }
    if (ans == -1)
    {
        cout << 1 << endl;
    }
    else
    {
        string str1 = s1, str2 = s2;
        str1.erase(ans,1);
        str2.erase(ans,1);
        if (dfs(s2, str1) || dfs(s1, str2))
        {
            cout << 1 << endl;
        }
        else
		{
			cout << 0 << endl;
		} 
    }
    return 0;
}

 第三题 走不出的迷宫

     还是把dfs转化为dp,其实没那么难,注意起始位置只能是1,1

#include <bits/stdc++.h>
using namespace std;
const int MAX = 105;
int h, w, ans;
char arr[MAX][MAX];
int dp[MAX][MAX];
int main()
{
	cin >> h >> w;
	for(int i = 1; i <= h; ++i)
	{
		for(int j = 1; j <= w; ++j)
		{
			char c;
			cin >> c;
			if(c == '.')
			{
				arr[i][j] = 1;
			}
		}
	}
	dp[1][1] = 1, ans = 1;
	//左和上的最大值
	for(int i = 1; i <= h; ++i)
	{
		for(int j = 1; j <= w; ++j)
		{
			if(arr[i][j])
			{
				if(arr[i][j-1] && dp[i][j-1])
				{
					dp[i][j] = max(dp[i][j], dp[i][j-1]+1);
					ans = max(ans, dp[i][j]);
				}
				if(arr[i-1][j] && dp[i-1][j])
				{
					dp[i][j] = max(dp[i][j], dp[i-1][j]+1);
					ans = max(ans, dp[i][j]);
				}
			}
		}
	}
	cout << ans << endl;
	return 0;
}

第四题 最长同余子数组

     不要理解错题意,同余可不是gcd。那这咋整??只要判断相邻两个数的差同于就行了,说是dp其实还是两重循环暴力求解,1E3的范围我还是能过的。但是再大就不行了。顺带说一下系统自带的gcd函数时间复杂度是log。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const signed M = 2E3+5;
int n, ans=1;
int arr[M], dp[M][M];	//i到j的序列的最大公约数
//问题出在哪里??就是这里的同余问题不一定是整除
//比如说都是奇数,这也可以的。
signed main()
{
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		cin >> arr[i];
	}
	memset(dp, 1, sizeof(dp));	//初始化为1
	for(int len = 2; len <= n; ++len)
	{
		for(int j = 1, e; j+len-1 <= n; ++j)
		{
			e = j+len-1;
			if(len == 2)
			{
				dp[j][e] = abs(arr[j]-arr[e]);
			}
			else
			{
				dp[j][e] = __gcd(dp[j][e-1], dp[e-1][e]);
			}
			if(dp[j][e] > 1)
			{
				ans = max(ans, len);
			}
		}
	}
	cout << ans << endl;
	return 0;
}

第五题 互质

    比较难,暴力的话肯定会超时, 因此需要考虑分解质因数。首先来找到1-100500中的所有质数,可以通过每个数划掉他的倍数的方法来做,不用打表。同时也能得到每个数最大的质因数。分解质因数时,只用每次除以最大的质因数就好了。这是一种非常巧妙的方法。

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
const int N = 100500;
bool mymap[N], prime[N], st[N];    //st是判断的质数,1为合数,prim记录分解得到的质因数
int maxp[N];                       //最大的质因数
signed main()
{
    int n, m, x;
    cin >> n >> m;
    maxp[1] = 1;
    for (int i = 2; i <= N; i++)
    {
        if (st[i])continue;
        for (int j = i*2; j <= N ; j+=i)
        {
            st[j] = true;
            maxp[j] = i;
        }
        maxp[i] = i;
    }
    for (int i = 0; i < n; i++)
    {
        int x;
        cin >> x;
        if (mymap[x])continue;
        mymap[x] = true;
        while (x != 1)
        {
            prime[maxp[x]] = 1;
            x /= maxp[x];
        }
    }
    vector<int>k;
    k.push_back(1);
    for (int i = 2; i <= m; i++)
    {
        if (prime[i] && !st[i])
            for (int j = i; j <= m; j += i)
                mymap[j] = 1;
    }
    for (int i = 2; i <= m; i++)
        if (!mymap[i])
            k.push_back(i);
    cout << k.size() << endl;
    for (auto i : k)
    {
        cout << i << endl;
    }
    return 0;
}

第六题 排队

     这道题难度最大。首先让两个序列按照顺序排列,让第二个序列按照第一个序列的顺序来排列,最后用归并排序求逆序数。

#include <bits/stdc++.h>
using namespace std;
const int max_n = 1e5;
const long long mod_num = 1e8 - 7;

struct node { int idx, val; } a[max_n], b[max_n];
long long ans = 0;
int temp[max_n];

void merge_sort(node arr[], int l, int r) {
	if (l == r) return;

	int m = l + r >> 1;
	merge_sort(arr, l, m);
	merge_sort(arr, m + 1, r);

	long long i = l, j = m + 1, k = l;
	while (i <= m && j <= r) {
		if (arr[i].val > arr[j].val) {
			temp[k] = arr[j].val;
			j++, k++;
			ans = (ans + m - i + 1LL) % mod_num;//累计逆序数
		}
		else {
			temp[k] = arr[i].val;
			i++, k++;
		}
	}

	while (i <= m) temp[k++] = arr[i++].val;
	while (j <= r) temp[k++] = arr[j++].val;
	for (i = l; i <= r; i++) arr[i].val = temp[i];
}

int main() {
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		//scanf("%d", a + i);
		cin >> a[i].val;
		a[i].idx = i;
	}
	for (int i = 0; i < n; i++) {
		//scanf("%d", b + i);
		cin >> b[i].val;
		b[i].idx = i;
	}

	sort(a, a + n, [](node n1, node n2) {
		return n1.val < n2.val;
		});
	sort(b, b + n, [](node n1, node n2) {
		return n1.val < n2.val;
		});

	for (int i = 0; i < n; i++) {
		a[i].val = b[i].idx;
	}
	sort(a, a + n, [](node n1, node n2) {
		return n1.idx < n2.idx;
		});

	merge_sort(a, 0, n - 1);
	cout << ans << endl;
	return 0;
}

第七题 最短路计数

    由于没有边权,最短路计数问题不能直接用迪杰斯特拉算法来求解,但是可以用bfs来求解。首先bfs得到每个点的最短路,然后dp求解最短路条数。dp的话就对每个点进行最短路计数。

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define endl '\n'
using namespace std;
vector<pair<int,int> >E[1000005];
bool vis[1000005];
int dis[1000005],ans[1000005];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(ans,0,sizeof(ans));
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int a,b;
        cin>>a>>b;
        E[a].push_back(make_pair(b,1));
        E[b].push_back(make_pair(a,1));
    }
    queue<int>q;
    dis[1]=0;
    ans[1]=1;
    q.push(1);
    vis[1]=1;
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=0;i<E[now].size();i++)
        {
            int v=E[now][i].first;
            if(dis[v]>dis[now]+E[now][i].second)
            {
                dis[v]=dis[now]+E[now][i].second;
                ans[v]=(ans[now])%100003;
                if(vis[v])continue;
                vis[v]=1;
                q.push(v);
            }
            else if(dis[v]==dis[now]+E[now][i].second){
                ans[v]=(ans[now]+ans[v])%100003;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(dis[i]!=inf)cout<<ans[i]<<endl;
        else cout<<0<<endl;
    }
    return 0;
}

第八题 最后的舞会

     搜索题,直接用next_permutation出答案太慢所以需要手写搜索算法。分治递归函数算法可以把结果当作参数来传递。可以先从1开始然后枚举下一个点。之后根据vis的情况枚举没被访问最小的点,然后再枚举下一个配对点的可能情况,再dfs。这里保证每次配对的点第一个点都比下一次配对的点第一个点的数值小,同时第一个点都比第二个点小,这样就不会出现迷乱的现象。

#include <bits/stdc++.h>
using namespace std;
bool vis[20];
int a[20][20];
int ans, n;
void dfs(int step, int res) // step为当前已经匹配的对数,res是当前的异或和
{
    if (step == n) //已全部匹配更新答案
    {
        ans = max(ans, res);
        return;
    }
    int i;
    for (i = 2; i <= n + n - 1; i++)
    {
        if (!vis[i]) //找到1~2n中第一个未被匹配的数(剪枝)
            break;
    }
    for (int j = i + 1; j <= n + n; j++)
    {
        if (vis[j] == 0) 			//未被匹配
        {
            vis[i] = 1, vis[j] = 1; //匹配
            dfs(step + 1, res ^ a[i][j]);
            vis[i] = 0, vis[j] = 0; //取消标记
        }
    }
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n + n - 1; i++)
        for (int j = i + 1; j <= n + n; j++)
            cin >> a[i][j];
    vis[1] = 1;                      // 1肯定会被匹配,直接加入匹配(剪枝)
    for (int i = 2; i <= n + n; i++) //枚举与1匹配的位置
    {
        vis[i] = 1;
        dfs(1, a[1][i]);
        vis[i] = 0;
    }
    cout << ans;
}

第九题 倒数第n个字符串

    26进制数的计算,可以转化为10进制然后再进行减法计算最后转为26进制显示,注意要把0显示为a,不能跳过。

#include <bits/stdc++.h>
#define int long long
using namespace std;

int l, n, m;
//正序分解
void to_as(int num)
{
	stack<char> s;
	for(int i = 1; i <= l; ++i)
	{
		s.push(num%26+'a');
		num /= 26;
	}
	while(!s.empty())
	{
		cout << s.top();
		s.pop();
	}
}
signed main()
{
	cin >> l >> n;
	for(int i = 1, now = 25; i <= l; ++i)
	{
		m = m*26+now;
	}
	to_as(m-n+1);
	return 0;
}

第十题 排队

     可以当作图来存储边,有两种情况不能够组队:

     1.一个点度数大于等于3。

     2.存在成环的现象。

     可以使用拓扑排序,由于本人对拓扑排序不很熟,所以加了注释代码。

#include <bits/stdc++.h>
using namespace std;

int n, m, f=1;
const int X=1E5+5;
int cnt[X];			//存放节点的度
vector<int> vec[X]; //存放边
int vis[X];			//避免重复访问节点
//拓扑排序的基本步骤
//1.把度数<=1(有向入度为0)的节点都放入队列
//2.从队列头部取出元素,并将相邻节点度数-1
//3.如果节点度数<=1并且没有访问过那么就放入队列
//4.重复上述操作直到队列为空
//5.最终如果所有节点都被访问就是没有环否则就有环
void top()
{
	queue<int> node;
	int num = 0;
	for(int i = 1; i <= n; ++i)		//初始化
	{
		if(cnt[i] <= 1)
		{
			node.push(i);
			vis[i] = 1;
		}
	}
	while(!node.empty())
	{
		int t = node.front();
		num++;
		node.pop();
		for(auto x: vec[t])
		{
			cnt[x]--;
			if(cnt[x] <= 1 && !vis[x])
			{
				node.push(x);
				vis[x] = 1;
			}
		}
	}
	if(num == n)
	{
		f = 1;
	}
	else
	{
		f = 0;
	}
}
int main()
{
	cin >> n >> m;
	if(m >= n)
	{
		cout << "No" << endl;
		return 0;
	}
	while(m--)
	{
		int a, b;
		cin >> a >> b;
		cnt[a]++;
		cnt[b]++;
		vec[a].push_back(b);
		vec[b].push_back(a);
		if(cnt[a]>2 || cnt[b]>2)
		{
			f = 0;
//			break;
		}
	}
	if(!f)
	{
		cout << "No" << endl;
	}
	else
	{
		top();
		if(f)
		{
			cout << "Yes" << endl;
		}
		else
		{
			cout << "No" << endl;
		}
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值