CSP-J模拟赛day5——解析+答案

题解

T1 三点连通块

考虑每个点作为中心时,方案数量实际上就是从相邻点中任意选两个组成三点连通块。

因此统计每个点的度,记为 d [ i ] d[i] d[i],对每个点来说方案数是 d [ i ] ∗ ( d [ i ] − 1 ) / 2 d[i] * (d[i] - 1) / 2 d[i]∗(d[i]−1)/2。

加和即可。注意long long。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

long long son[100010],k;
long long q[100010];
int main(){
	int n;
	cin>>n;
	for (int i=1;i<n;i++){
		int a,b;
		cin>>a>>b;
		son[a]++;
		son[b]++;
	}
	for (int i=2;i<=n;i++){
		q[i]=q[i-1]+i-1;
	}
	for (int i=1;i<=n;i++){
		k+=q[son[i]];
	}
	cout<<k;
	return 0;
}

T2 矩阵魔法

普通的模拟。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

int n,m;
int a[1505][1505];
int b[1505][1505];
int cnt=0;
void right(int x,int y,int r){
	int sx=x-r,sy=y-r;
	int ex=x+r,ey=y+r;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			b[i][j]=a[i][j];
		}
	}
	for(int i=sx;i<=ex;i++){
		for(int j=sy;j<=ey;j++){
			b[x-y+j][x+y-i]=a[i][j];
			//b[x+y-j][y-x+i]
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			a[i][j]=b[i][j];
		}
	}
}
void left(int x,int y,int r){
	int sx=x-r,sy=y-r;
	int ex=x+r,ey=y+r;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			b[i][j]=a[i][j];
		}
	}
	for(int i=sx;i<=ex;i++){
		for(int j=sy;j<=ey;j++){
			b[x+y-j][y-x+i]=a[i][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			a[i][j]=b[i][j];
		}
	}
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			cnt++;
			a[i][j]=cnt;
		}
	}
	for(int i=1;i<=m;i++){
		int x,y,r,z;
		scanf("%d%d%d%d",&x,&y,&r,&z);
		if(z==0){
			right(x,y,r); 
		}else{
			left(x,y,r); 
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			printf("%d ",a[i][j]);
		}
		printf("\n");
	}

	return 0;
}

T3 汪汪队立大功

先不考虑后面可能有机关跳过的问题,考虑每个机会使用后带来的影响:

首先不需要消耗这个机关的能量值,但是后续的所有机关都需要加一,所以减少的能量消耗为 a [ i ] − ( n − i ) a[i] - (n - i) a[i]−(n−i)

然后你会发现选 k k k个机关的话,这 k k k个机关自己多消耗的加一是不存在的(因为被跳过了)

所以刚才算的答案比实际的消耗多了$ k * (k - 1) / 2$的能量,减掉即为答案。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

long long n,k,a[1010],dp[1010][1010];

int main(){
//	freopen("dog.in","r",stdin);
//	freopen("dog.out","w",stdout);
	int t;
	cin>>t;
	while (t--){
		cin>>n>>k;
		for (int i=1;i<=n;i++){
			cin>>a[i];
		}
		for (int i=0;i<=n;i++){
			for (int j=0;j<=k;j++){
				dp[i][j]=INT_MAX;
			}
		} 
		for (int i=0;i<=k;i++){
			dp[0][i]=0; 
		} 
		for (int i=1;i<=n;i++){
			for (int j=0;j<=k;j++){
				if(j==0)dp[i][j]=dp[i-1][j]+a[i]; 
				else dp[i][j]=min(dp[i-1][j-1],dp[i-1][j]+a[i]+j);
			}
		}
		cout<<dp[n][k]<<endl;
	}
	return 0;
}

T4 避水珠

首先要观察出一个结论:虽然不限制玩家持有避水珠的数量,但出于最小化的角度考虑,任何时候玩家最多持有一个避水珠。

考虑DP。设状态为 d p [ i ] [ j ] dp[i][j] dp[i][j]表示在点 i i i处持有第j个避水珠时的最小能量消耗。其中 j = 0 j=0 j=0时表示不持有任何避水珠。

转移考虑下列情况:

  1. 当这段路不是雨区,可以不持有 $dp[i + 1][0] = min(dp[i + 1][0], dp[i][j]) $
  2. 当有一个避水珠时,可以保持 d p [ i + 1 ] [ j ] = m i n ( d p [ i + 1 ] [ j ] , d p [ i ] [ j ] + c o s t [ j ] ) dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + cost[j]) dp[i+1][j]=min(dp[i+1][j],dp[i][j]+cost[j])
  3. 如果这个位置有新的避水珠,可以换掉 d p [ i + 1 ] [ b i d [ i ] ] = m i n ( d p [ i + 1 ] [ b i d [ i ] ] , d p [ i ] [ j ] + c o s t [ b i d [ i ] ] ) dp[i + 1][bid[i]] = min(dp[i + 1][bid[i]], dp[i][j] + cost[bid[i]]) dp[i+1][bid[i]]=min(dp[i+1][bid[i]],dp[i][j]+cost[bid[i]]),其中 b i d [ i ] bid[i] bid[i]表示出现在点 i i i处的最小重量的避水珠编号。

最后在 d p [ a ] [ 1 ] dp[a][1] dp[a][1] ~ d p [ a ] [ m ] dp[a][m] dp[a][m] 之间取一个最小的答案即可。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e3 + 5;
const int INF = 0x3f3f3f3f;

int rain[maxn], bid[maxn], cost[maxn], dp[maxn][maxn];
int a, n, m;

int main() {
	cin >> a >> n >> m;
	for(int i = 1; i <= n; ++i) {
		int l, r; cin >> l >> r;
        ++rain[l]; --rain[r];
	}
    for(int i = 1;i <= a; ++i)
        rain[i] += rain[i - 1];
	
	for (int i = 1; i <= m; ++i) {
		int x, p; cin >> x >> p;
		cost[i] = p;
        if(bid[x] == 0 || cost[bid[x]] > p) 
            bid[x] = i;
	}
	memset(dp, 0x3f, sizeof dp);

    dp[0][0] = 0;
    for(int i = 0;i < a; ++i) {
        for(int j = 0;j <= m; ++j) {
            if(dp[i][j] == INF) continue;
            if(rain[i] == 0) dp[i + 1][0] = min(dp[i + 1][0], dp[i][j]);
            if(j) dp[i + 1][j] = min(dp[i + 1][j], dp[i][j] + cost[j]);
            if(bid[i]) dp[i + 1][bid[i]] = min(dp[i + 1][bid[i]], dp[i][j] + cost[bid[i]]);
        }
    }

	int ans = INF;
	for (int i = 0; i <= m; ++i)
		ans = min(ans, dp[a][i]);
	cout << ans << '\n';
	return 0;
}
相关推荐
闻缺陷则喜何志丹9 分钟前
【C++动态规划 图论】3243. 新增道路查询后的最短距离 I|1567
c++·算法·动态规划·力扣·图论·最短路·路径
TENET信条19 分钟前
代码随想录 day52 第十一章 图论part03
图论
Lenyiin28 分钟前
01.02、判定是否互为字符重排
算法·leetcode
鸽鸽程序猿43 分钟前
【算法】【优选算法】宽搜(BFS)中队列的使用
算法·宽度优先·队列
Jackey_Song_Odd44 分钟前
C语言 单向链表反转问题
c语言·数据结构·算法·链表
Watermelo6171 小时前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
乐之者v1 小时前
leetCode43.字符串相乘
java·数据结构·算法
A懿轩A2 小时前
C/C++ 数据结构与算法【数组】 数组详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·考研·算法·数组
古希腊掌管学习的神2 小时前
[搜广推]王树森推荐系统——矩阵补充&最近邻查找
python·算法·机器学习·矩阵
云边有个稻草人2 小时前
【优选算法】—复写零(双指针算法)
笔记·算法·双指针算法