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