Have_No_W OI R1题解

Solution

T1

题意

题面链接

\(n\) 个数,判断有多少个数小于 \(k\)

签到题,没什么好说的。for 扫一遍统计即可。

Code

#include <iostream>
#include <cstdio>

using namespace std;

int ans,k,n;

int main() {
	scanf("%d%d",&k,&n);
	for(register int i = 1; i <= n; i++) {
		int a;
		scanf("%d",&a);
		if(a>k) ans++;
	}
	printf("%d\n",ans);
	return 0;
}

T2

题意

题面链接

\(h\) 米高的山,奇数小时爬,偶数小时休息。一次最多\(a\) 米。若停在休息点,休息时不动;否则下落 \(b\) 米。求爬到顶的最小用时。

注意:爬到顶后不用休息。

分析 & 实现

由于 \(a > b\),一定能登顶。并且注意到数据很小,其实可以直接模拟,但是要小小贪心一下。

考虑每次决策(以 2 小时为单位):

  1. 若可以直接登顶,直接加 1 小时,结束;
  2. 否则,考虑最高的可达休息点,比较爬到休息点的高度和爬 \(a\) 米回落后的高度,进行能爬得更高的决策。

注意要先把休息点排序。

Code

其实求最高的可达休息点可以二分,但是本题时间太够了,而本蒟蒻又懒,写个指针就完事了(乐)。

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

const int maxn=1.1e3;

int h,n,a,b,ans;
int s[maxn];

int main() {
	scanf("%d%d%d%d",&h,&n,&a,&b);
	for(register int i = 1; i <= n; i++) scanf("%d",&s[i]);
	sort(s+1,s+1+n);
	int nows=1,nowh=0;
	//nows:目前要考虑的第一个休息点  nowh:目前高度
	while(nowh<h) {
		if(nowh+a>=h) nowh=h,ans++;
		else {
			int maxs=0;//最高的休息点高度
			for(register int i = nows; i <= n&&s[i]<=nowh+a; i++) {
				maxs=s[i],nows=i;
			}
			if(maxs>nowh+a-b) nowh=maxs;
			else nowh+=a-b;
			ans+=2;
		}
	}
	printf("%d\n",ans);
	return 0;
}

T3

题意

题面链接

判断关于 \(x,y\) 的不定方程方程 \(ax + by = c\) 是否有整数解。

(二毛:他们要我出个数学题)

分析

我们直接点,这其实是一个数学题。本题考察裴蜀定理。详情自见 oiwiki,本文只用结论,不进行证明。

裴蜀定理:\(\operatorname{gcd}(a,b)\mid ax + by\),且存在 \(x,y\) 使 $ ax + by=\operatorname{gcd}(a,b)$。

其中,\(\operatorname{gcd}(a,b)\) 表示 \(a,b\) 的最大公因数,竖线是整除。

对于本题,当且仅当 \(\operatorname{gcd}(a,b)\mid c\) 时,关于 \(x,y\) 的不定方程方程 \(ax + by = c\) 是有整数解。

证明:

充分性:由于 $ ax + by=\operatorname{gcd}(a,b)$,所以任何满足 \(\operatorname{gcd}(a,b)\mid c\)\(c\) 都可由上式直接扩大得到。具体地,设 \(c=k\operatorname{gcd}(a,b)\)\(k\) 为整数,$ ax + by=\operatorname{gcd}(a,b)$,故 $ a(kx) + b(ky)=k\operatorname{gcd}(a,b)=c$,即原方程有整数解 \((kx,ky)\)

必要性:\(\operatorname{gcd}(a,b)\mid ax + by\),故 \(c\) 一定满足 \(\operatorname{gcd}(a,b)\mid c\)

证讫。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

int n,a,b,c;

int main() {
	scanf("%d",&n);
	for(register int i = 1; i <= n; i++) {
		scanf("%d%d%d",&a,&b,&c);
		if(!(c%__gcd(a,b))) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

T4

题意

题面链接

用一个容量为 \(t\) 的背包装两类物品,一类为简单的 01 物品,另一类为有依赖性的物品。对于有依赖性的物品,具体地,每两个物品 \(1,2\) 有一个依赖关系,要装 \(2\) 必须先装 \(1\)。求最大价值。

分析

也是直接说清楚了,这就是个背包(dp 的一种),但是有两种物品。很朴素地,我们可以分开想:

01物品

这里只简单提一下,没写过 01 背包的最好去 P1048 先看看,不然着看这里是看不懂的。

定义 \(dp(i,j)\) 表示考虑\(i\)物品,用\(j\) 的容量的最大价值(状态,对应 \((i,j)\) 即为阶段)。

状态转移方程:

\[dp(i,j)=\begin{cases} dp(i-1,j) & j < w \\ max(dp(i-1,j),dp(i-1,j-w)+v) &j\ge w\\ \end{cases} \]

其中,\(w\) 为当前物品占用,\(v\) 为当前物品价值。

边界:\(dp(i,0)=0\),即不用容量什么都不能装。

目标:\(dp(n,t)\),即用所有容量,考虑完所有物品。

直观地解释一下:我们假设任意 \((x,y)\) 满足 \(x \le i\) 的状态都已经处理。式即:如果当前可用空间不够装当前物品(\(j<w\)),不装;如果够,尝试装(\(j-w\),其实相当于将空间占用了,去前面找能装下当前物体的情况),看看是否比不装好。

接下来是滚动优化。

注意到上式 \(dp(i,j)\) 只与 \(dp(i-1,j)\) 有关,所以 \(dp(i-2,j)\) 之类的是没用的,可以直接舍弃。为了区别 \(dp(i,j)\)\(dp(i-1,j)\),我们可以保留一个第一维为 \(2\) 的数组,或者设计顺序使得用到的 \(dp(j-w)\) 一定是未被当前物品更新过的。由于 \(j-w<j\),即每次只往前面找,故倒序枚举 \(j\) 即可。

依赖性物品

对于这种物品,我们可以合并:将两个物品看作两个互斥的物品(\(1\)\(1,2\)),从而转化为分组物品。分组物品一组只能选一个,整组相当于 01 物品。我们考虑到这一组时,整组扫一遍,保证只用一个就行了。

Code

#include <iostream>
#include <cstdio>

using namespace std;

const int maxn=1.1e3,maxt=2.2e4;

int g[maxn*2][3];
//分组后物品组,把01物品视作组里只有1个东西的物品
int w[maxn*4],v[maxn*4];
int dp[maxt];

int t,n,m;

int main(){
    scanf("%d%d%d",&t,&n,&m);
    for(register int i = 1; i <= n; i++){
    	int ai,bi,wi;
        scanf("%d%d%d",&ai,&bi,&wi);
        v[i*2-1]=wi,w[i*2-1]=ai;
        v[i*2]=wi*2,w[i*2]=ai+bi;
        //依赖性转分组
    }
    for(register int i = n+1; i <= n+m; i++) {
		int ci,vi;
		scanf("%d%d",&ci,&vi);
		v[i+n]=vi,w[i+n]=ci;
	}
    for(register int i = 1; i <= n+m; i++){
        for(register int j = t; j; j--){//倒序枚举每组
            for(register int k = 1; k <= 1+(i<=n); k++){//考虑组中每个物品
            	int now=(i<=n)?((i-1)*2+k):(i+n);//还原组中物品下标
                if(j >= w[now]){
                    dp[j]=max(dp[j],dp[j-w[now]]+v[now]);//和01一样的转移
                }
            }
        }
    }
    cout<<dp[t];
    return 0;
}

闲话

完结撒花!!!

posted @ 2025-05-18 11:57  Tenil  阅读(18)  评论(1)    收藏  举报