「EZEC」 Round1 开学信心赛【题解】

A
模拟操作题

#include
using namespace std;

#define ll long long
#define gc getchar

inline int read(){
	int f=1,res=0;
	char ch=gc();
	while(!isdigit(ch)) f^ch=='-',ch=gc();
	while(isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=gc();
	return f?res:-res;
}

const int N=1010;
int f[N];
int path[N][N];
int a[N];

int n,m,k; 
int v[N],w[N];
int main(){
	n=read(),m=read(),k=read();
	for(int i=1;i<=n;i++) 
		v[i]=read(),w[i]=read();
	for(int i=1;i<=n;i++)
		for(int j=m;j>=w[i];j--){
			if(f[j]<f[j-w[i]]+v[i]) {
				f[j]=f[j-w[i]]+v[i];
				path[i][j]=1;
			}
		}
	int cnt=0;
	int p=n,q=m;
	while(p>=1&&q>=0){
		if(path[p][q]){
			a[++cnt]=v[p];
			q-=w[p];
		}
		p--;
	}
	sort(a+1,a+cnt+1); 
	int sum=0;
	if(cnt<n){
		for(int i=cnt;i;i--){
			if(k>0) {
				k--;
				sum+=a[i];
			}
			sum+=a[i];
		}
		printf("%d\n",sum);
	}
	else{
		for(int i=cnt;i>1;i--){
			if(k>0) {
				k--;
				sum+=a[i];
			}
			sum+=a[i];
		}
		printf("%d\n",sum);
	}
	return 0;
}

B
简单的背包题,对父母要加特殊判断,如果背包能装下全部东西,去掉价值最小的。
如果没能装下全部东西,则把背包的价值计算起来就可以了。

#include
using namespace std;

#define ll long long
#define gc getchar

inline int read(){
	int f=1,res=0;
	char ch=gc();
	while(!isdigit(ch)) f^ch=='-',ch=gc();
	while(isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=gc();
	return f?res:-res;
}

const int N=1010;
int f[N];
int path[N][N];
int a[N];

int n,m,k; 
int v[N],w[N];
int main(){
	n=read(),m=read(),k=read();
	for(int i=1;i<=n;i++) 
		v[i]=read(),w[i]=read();
	for(int i=1;i<=n;i++)
		for(int j=m;j>=w[i];j--){
			if(f[j]<f[j-w[i]]+v[i]) {
				f[j]=f[j-w[i]]+v[i];
				path[i][j]=1;
			}
		}
	int cnt=0;
	int p=n,q=m;
	while(p>=1&&q>=0){
		if(path[p][q]){
			a[++cnt]=v[p];
			q-=w[p];
		}
		p--;
	}
	sort(a+1,a+cnt+1); 
	int sum=0;
	if(cnt<n){
		for(int i=cnt;i;i--){
			if(k>0) {
				k--;
				sum+=a[i];
			}
			sum+=a[i];
		}
		printf("%d\n",sum);
	}
	else{
		for(int i=cnt;i>1;i--){
			if(k>0) {
				k--;
				sum+=a[i];
			}
			sum+=a[i];
		}
		printf("%d\n",sum);
	}
	return 0;
}

C
拿了个暴力20分!
dp做法:
首先将美味值排序
d p i , j dp_{i,j} dpi,j以第i种结尾选了j的种的方案数
可以发现 d p i , j dp_{i,j} dpi,j ∑ a p ∗ l < = a i a p ∗ r > = a i d p p . j − 1 \sum_{a_p*l<=a_i}^{ap*r>=a_i}dp_{p.j-1} apl<=aiapr>=aidpp.j1
这个过程三重for 循环模拟可解为 ∑ i = k n d p i , k \sum_{i=k}^ndp_{i,k} i=kndpi,k
然而只能得到40分
我们可以设 d p i , j dp_{i,j} dpi,j为到第i中选了j种的方案数
首先很显然 d p i − 1 , j → d p i , j dp_{i-1,j}\to dp_{i,j} dpi1,jdpi,j
然后我们只需知道对于每个 i 最大的 p 和最小的 p,满足 a p ∗ l < = a i , a p ∗ r > = a i a_p*l<=a_i,a_p*r>=a_i apl<=ai,apr>=ai
这个过程我们可以O(n)预处理得到

int mi[N],ma[N];
int li=0,la=0;
for(int i=1;i<=n;i++){
	while(va[la+1]*l<=va[i]&&la<i-1) la++;
	while(va[li+1]*r<va[i]&&li<i-1) li++;
	mi[i]=li;
	ma[i]=la;
	dp[i][1]=i;//初始化
}

其中 m a i ma_i mai表示最大的p使得 a p ∗ l < = a i a_p*l<=a_i apl<=ai m i i mi_i mii表示最大的q使得 a q ∗ r < a i a_q*raqr<ai(这样保证 a q + 1 ∗ r > = a i a_{q+1}*r>=a_i aq+1r>=ai)
那么 ∑ a p l < = a i a p ∗ r > = a i d p p , j − 1 \sum_{a_pl<=a_i}^{a_p*r>=_ai}dp_{p,j-1} apl<=aiapr>=aidpp,j1
就是 d p m a i , j − 1 − d p m i i , j − 1 dp_{ma_i,j-1}-dp_{mi_i,j-1} dpmai,j1dpmii,j1
综上转移状态方程就是

dp[i][j] = (dp[i-1][j] + dp[ma[i]][j-1] - dp[mi[i]][j-1] + mod) % mod;

由于 m o d × 2147483647 mod\times2147483647 mod×2147483647 , 所有变量只需要用 int,边算边模,然后开滚动数组即可。
注意:
转移中每次要第从 1 开始遍历到 n ,以重置过期状态,否则会受到以前状态的影响。
对于问题2
还是 dp, 转移方程也仅有一行。
f i , j 为 到 第 i 中 选 了 j 种 的 最 大 美 味 值 的 和 f_{i,j}为到第i中选了j种的最大美味值的和 fi,jij
很显然还是 f i − 1 , j → f i , j f_{i-1,j}\to f_{i,j} fi1,jfi,j
然后发现 f m a i , j − 1 + a i → f i , j f_{ma_i,j-1}+a_i\to f_{i,j} fmai,j1+aifi,j两者取最大值即可。
但仅当以第 i种结尾选 k 种的方案存在时,我们才能考虑后者。
所以转移方程为:

f[i][j&1]=max(f[i-1][j&1],(dp[i][j&1]!=dp[i-1][j&1]?f[ma[i]][j&1^1]+va[i]:0))%mod;

综上

#include
using namespace std;

#define ll long long
#define gc getchar
#define pb push_back

inline int read(){
	int f=1,res=0;
	char ch=gc();
	while(!isdigit(ch)) f^ch=='-',ch=gc();
	while(isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=gc();
	return f?res:-res;
}

const int N=2000005;
const int mod=1e9+7;

int n,k;
int l,r;
int va[N];
int mi[N],ma[N];
int dp[N][2];
int f[N][2];
int la,li;

int main(){ 
    n=read(),k=read(),l=read(),r=read();
    for(int i=1;i<=n;i++)va[i]=read();
    sort(va+1, va+1+n);
    for(int i=1;i<=n;i++) {
        while(va[la+1] * l <= va[i] && la+1 < i)la++;
        while(va[li+1] * r < va[i] && li+1< i)li++;
        mi[i]=li;
        ma[i]=la;
        dp[i][1]=i;
        f[i][1]=va[i];
    }

    for(int j=2;j<=k;j++) {
        for(int i=1;i<=n;i++) {
            if(dp[ma[i]][j&1^1] < dp[mi[i]][j&1^1])
                dp[i][j&1] = (dp[i-1][j&1] + dp[ma[i]][j&1^1] - dp[mi[i]][j&1^1] + mod ) % mod;
            else
                dp[i][j&1] = (dp[i-1][j&1] + dp[ma[i]][j&1^1] - dp[mi[i]][j&1^1] ) % mod;

            f[i][j&1] = max(f[i-1][j&1], (dp[i][j&1] != dp[i-1][j&1] ? f[ma[i]][j&1^1] + va[i] : 0 ) ) % mod;
        }
    }

    cout<<dp[n][k&1]<<endl<<f[n][k&1];
}

 

你可能感兴趣的:(洛谷比赛)