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

题解

T1 三点连通块

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

因此统计每个点的度,记为 d i di di,对每个点来说方案数是 d i ∗ ( d i − 1 ) / 2 di * (di - 1) / 2 di∗(di−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 ) ai - (n - i) ai−(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 dpij dpij表示在点 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 ) dpi + 1j = min(dpi + 1j, dpij + costj) dpi+1j=min(dpi+1j,dpij+costj)
  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 ] ) dpi + 1bid\[i] = min(dpi + 1bid\[i], dpij + costbid\[i]) dpi+1bid\[i]=min(dpi+1bid\[i],dpij+costbid\[i]),其中 b i d i bidi bidi表示出现在点 i i i处的最小重量的避水珠编号。

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

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;
}
相关推荐
一个儒雅随和的男子11 分钟前
限流算法详细剖析
java·服务器·算法
工业胶粘剂技术1 小时前
单组分高温环氧结构胶 K-EP280 完整技术参数与工程选型分析
算法·制造
汉克老师1 小时前
GESP6级C++考试语法知识(五十五、动态规划----背包问题(八、混合背包)
c++·动态规划·dp·背包问题·gesp六级·混合背包问题
小欣加油2 小时前
Leetcode31 下一个排列
数据结构·c++·算法·leetcode·职场和发展
_日拱一卒2 小时前
LeetCode:39组合总和
java·算法·leetcode·职场和发展
无限进步_2 小时前
【Linux】进程状态、僵尸与孤儿、进程调度
linux·运维·服务器·开发语言·数据结构·算法
郝学胜-神的一滴2 小时前
力扣 662 :二叉树最大宽度
java·数据结构·c++·python·算法·leetcode·职场和发展
2301_764441332 小时前
基于Stackelberg博弈的分散式库存模型
python·算法·数学建模
qq 13740186113 小时前
医用无菌屏障系统加速老化标准解读:ASTM F1980-2016 全解析
人工智能·算法·加速老化·包装测试·astm·医疗器械包装·无菌屏障系统
wayz113 小时前
Overlap:SLOPE(线性回归斜率)技术指标详解
算法·金融·数据分析·回归·线性回归·量化交易·特征工程