国庆集训赛-1006

问题 A: 网络路由

广度优先搜索,并且在遍历到每一个点的时候储存一下每一个点的上一个节点,就可以得到最短的路径。

#include <bits/stdc++.h>
 
using namespace std;
const int maxN = 1e5+1;
 
int N, M, K, a, b, p[maxN], dist[maxN];
bool vis[maxN];
vector<int> G[maxN];
queue<int> Q;
 
int main(){
    scanf("%d %d", &N, &M);
    for(int i = 0; i < M; i++){
        scanf("%d %d", &a, &b);
        G[a].push_back(b);
        G[b].push_back(a);
    }
 
    Q.push(1);
    vis[1] = true;
    while(!Q.empty()){
        int u = Q.front(); Q.pop();
        for(int v : G[u]){
            if(!vis[v]){
                dist[v] = dist[u]+1;
                vis[v] = true;
                p[v] = u;
                Q.push(v);
            }
        }
    }
 
    if(!vis[N]){
        printf("IMPOSSIBLE\n");
        return 0;
    }
 
    int u = N;
    K = dist[N];
    vector<int> ans(K+1);
    for(int i = K; i >= 0; i--){
        ans[i] = u;
        u = p[u];
    }
 
    printf("%d\n", K+1);
    for(int i = 0; i <= K; i++)
        printf("%d%c", ans[i], (" \n")[i==K]);
}

问题 B: 硬币问题

记 dp[i] 表示凑出总和为 i最少需要多少个硬币

转移方程:

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using std::cin;
using std::cout;
using std::vector;

inline void chmin(int& x, int y) { if (x > y) x = y; }

int main() {
    int n, x;
    cin >> n >> x;

    vector<int> c(n);
    rep(i, n) cin >> c[i];

    const int INF = 1001001001;
    vector<int> dp(x+1, INF);
    dp[0] = 0;
    for (int v : c) {
        for (int j = v; j <= x; ++j) {
            chmin(dp[j], dp[j-v] + 1);
        }
    }

    cout << (dp[x] == INF ? -1 : dp[x]) << '\n';

    return 0;
}

问题 C: 硬币组合 I

注意到每种硬币可以用无限次

记 dp[w] 表示凑出 w 的方案数

转移方程:

#include <bits/stdc++.h>
 
using namespace std;
typedef long long ll;
const int maxN = 100;
const int maxX = 1e6;
const ll MOD = 1e9+7;
 
int N, X, c[maxN];
ll dp[maxX+1];
 
int main(){
    scanf("%d %d", &N, &X);
    for(int i = 0; i < N; i++)
        scanf("%d", &c[i]);
 
    dp[0] = 1;
    for(int i = 0; i < X; i++)
        if(dp[i] != 0)
            for(int j = 0; j < N; j++)
                if(i+c[j] <= X)
                    dp[i+c[j]] = (dp[i+c[j]] + dp[i]) % MOD;
 
    printf("%lld\n", dp[X]);
}

问题 D: 硬币组合 II

记 dp[w] 表示凑出 w的有序方案数

转移方程:

我们只需改变前一个问题的嵌套循环顺序。
因为我们在总和之前循环金币的种类,我们只遍历了一次金币集合,所以不可能创建两个组合,使得同一组硬币以不同顺序排列

#include <bits/stdc++.h>
 
using namespace std;
typedef long long ll;
const int maxX = 1e6;
const ll MOD = 1e9+7;
 
int N, X, c;
ll dp[maxX+1];
 
int main(){
    scanf("%d %d", &N, &X);
 
    dp[0] = 1;
    for(int i = 0; i < N; i++){
        scanf("%d", &c);
        for(int j = 0; j <= X-c; j++)
            dp[j+c] = (dp[j+c] + dp[j]) % MOD;
    }
 
    printf("%lld\n", dp[X]);
}

问题 E: 交换翻转

如果k为奇数,将字符串排序后输出

如果k为偶数,将字符串奇数位置和偶数位置分别排序后输出

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e5 + 10;
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
 
    int t;cin >> t;
    while(t --)
    {
        int n, k;cin >> n >> k;
        string s;cin >> s;
        
        if(k%2 == 0)
        {
            sort(s.begin(), s.end());
            cout << s << "\n";
        }
        else
        {
            string a, b;
            for (int i = 0;i < n;i += 2)a += s[i];
            for (int i = 1;i < n;i += 2)b += s[i];
            sort(a.begin(), a.end());
            sort(b.begin(), b.end());
            
            for (int i = 0; i < n / 2; i ++ )cout << a[i] << b[i];
            if(n&1)cout << a[a.size() - 1];
            cout << "\n";
        }
    }
    return 0;
}

问题 F: 建造塔

#include <bits/stdc++.h>
 
using namespace std;
typedef long long ll;
const int maxN = 1e6;
const ll MOD = 1e9+7;
 
int T, N;
ll dp[maxN+1];
 
int main(){
    dp[1] = 2;
    dp[2] = 8;
    for(int i = 3; i <= maxN; i++)
        dp[i] = ((6*dp[i-1] - 7*dp[i-2]) % MOD + MOD) % MOD;
 
    scanf("%d", &T);
    for(int t = 0; t < T; t++){
        scanf("%d", &N);
        printf("%lld\n", dp[N]);
    }
}

问题 G: 公司查询I

LCA的简易版本,注释代码如下

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int n,m,x,y,k,s,cnt,f[N][21],dep[N],head[N];
//f[i][j]表示i的2^j祖先,也就是i向上跳2^j步的点。dep表示深度。
struct node
{
	int to,next;
}e[N<<1];
void add(int x,int y)
{
	e[cnt].to=y;
	e[cnt].next=head[x];
	head[x]=cnt++;
}
void dfs(int u,int father)//预处理得到f数组,u表示当前搜索到的点,father表示u的父节点
{
	dep[u]=dep[father]+1;//u的父节点深度+1就是u的深度
	f[u][0]=father;//u向上跳2^0步(1步),为father点
	for(int i=1;i<=20;i++)//u与其祖先的距离最大不超过2^20
		f[u][i]=f[f[u][i-1]][i-1];//dp的思想:u向上跳2^i步相当于u向上跳2^(i-1)步,再向上跳2^(i-1)步
	for(int i=head[u];i!=-1;i=e[i].next)//遍历与u相连的点
	{
		int v=e[i].to;
		if(v!=father)dfs(v,u);//如果当前遍历的点v不是father点,则可以向下继续搜索
	}
}
int get_fa(int x,int k)
{
	int t=dep[x]-k;
	for(int i=20;i>=0;i--)//倍增向上跳跃,求祖先
		if(dep[f[x][i]]>t)x=f[x][i];
	return f[x][0];
}
int main()
{
	ios::sync_with_stdio(false);
	cin>>n>>m;
	memset(head,-1,sizeof(head));
	for(int i=2;i<=n;i++)
	{
		cin>>x;
		add(x,i);
		add(i,x);
	}
	memset(f,0,sizeof(f));
	dfs(1,0);//从起点s开始搜索,设起点的父节点为0
	while(m--)
	{
		cin>>x>>k;
		int ot=get_fa(x,k);
		printf("%d\n",(ot==0?-1:ot));
	}
	return 0;
}

问题 H: 中位数

动态中位数模板题。

 建立一个小根堆,一个大根堆,小根堆的栈顶是数列中的最小值,大根堆的栈顶是数列中最大值,小根堆中元素要总是大于大根堆的元素,输出的元素始终是小根堆的栈顶(中位数)

#include <bits/stdc++.h>

using namespace std;
struct cmp1
{
	bool operator()(int x, int y) {
		return x > y;
	}
};
priority_queue<int> bq;//大根堆
priority_queue<int,vector<int>,cmp1> sq;//小根堆
int n,va;
void solve()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>va;
		if(bq.empty()) bq.push(va);
		else
		{
			if(va>bq.top()) sq.push(va);
			else bq.push(va);
		}
		if(bq.size()>sq.size()+1)
		{
			sq.push(bq.top());
			bq.pop();
		}
		while(sq.size()>bq.size())
		{
			bq.push(sq.top());
			sq.pop();
		}
		if(i%2)
			cout<<bq.top()<<"\n";
	}
	cout<<endl;
}
signed main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int T=1;
//	cin>>T;
	while(T--) solve();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值