• HZOI20190820模拟27题解


    A. 小奇挖矿2

    显然的O(m)dp:$f[i]=max(f[i-4],f[i-7])+a[i]$,当然你要保证i,i-4,i-7都能到达

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define re register
    using namespace std;
    const int MAXN=1e7+5;
    int n,m,a[MAXN],b[MAXN],ans=0,f[MAXN];
    signed main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++){
    		scanf("%d%d",&a[i],&b[i]);
    		f[b[i]]+=a[i];
    	}
    	for(int i=0;i<=3;i++) f[i]=-1;
    	for(int i=5;i<=6;i++) f[i]=-1;
    	ans=max(f[4],f[7]);
    	for(int i=8;i<=m;i++){
    		if(f[i-4]==-1&&f[i-7]==-1){
    			f[i]=-1;
    			continue;
    		}
    		f[i]+=max(f[i-4],f[i-7]);
    		ans=max(ans,f[i]);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    然后我们优化,我们发现如果两点之间的距离超过18就一定能到达,所以我们把距离压缩一下

    具体为什么你需要做一下NOIP2017小凯的疑惑

    然后就AC了

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define re register
    using namespace std;
    const int MAXN=2e6+5;
    int n,m,a[MAXN],b[MAXN],ans=0,f[MAXN];
    struct node{
    	int a,b;
    	friend bool operator < (node x,node y){
    		return x.b==y.b?x.a<y.a:x.b<y.b;
    	}
    }mine[MAXN];
    signed main(){
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    		scanf("%d%d",&mine[i].a,&mine[i].b);
    	sort(mine+1,mine+n+1);
    	for(int i=1;i<=n;i++){
    		if(mine[i].b-mine[i-1].b>17)
    			a[i]=mine[i].a,b[i]=b[i-1]+18;
    		else a[i]=mine[i].a,b[i]=b[i-1]+(mine[i].b-mine[i-1].b);
    	}
    	for(int i=1;i<=n;i++) f[b[i]]+=a[i];
    	for(int i=0;i<=3;i++) f[i]=-1;
    	for(int i=5;i<=6;i++) f[i]=-1;
    	m=b[n];
    	ans=max(f[4],f[7]);
    	for(int i=8;i<=m;i++){
    		if(f[i-4]==-1&&f[i-7]==-1){
    			f[i]=-1;
    			continue;
    		}
    		f[i]+=max(f[i-4],f[i-7]);
    		ans=max(ans,f[i]);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    还有一种解决方法:

    将所有矿物排序,f[i]为到第i个矿体能获得的最大原矿数,那么f[i]=max{f[j]}+x[i],其中,i-j满足能拆分成4和7。

    这样dp需要n2的复杂度,但是我们发现,相距超过17的一定可达,那么不超过17的范围内暴力,超过17的取个最大值即可,复杂度O(n)

    from skyh:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    using namespace std;
    const int N=1e5+10;
    map<int,int> mp;
    int n,m,ans,cnt;
    int dp[N],mx[N];
    bool ok[20]={1,0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1,0};//0 4 7 8 11 12 14 15 16  >17
    inline int read(register int x=0,register char ch=getchar(),bool ff=0){
    	while(!isdigit(ch)) ff=ch=='-',ch=getchar();
    	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    	return ff?-x:x;
    }
    struct node{
    	int val,pos;
    	friend bool operator < (const node &a,const node &b){
    		return a.pos<b.pos;
    	}
    }k[N];
    int main(){
    	//freopen("x.in","r",stdin);
    	//freopen("me.out","w",stdout);
    	n=read(); m=read();
    	for(int i=1,v,p,o;i<=n;++i){
    		v=read(),p=read();
    		if(!mp[p]) mp[p]=++cnt;
    		o=mp[p]; k[o].pos=p; k[o].val+=v; 
    	}
    	sort(k+1,k+cnt+1);
    	memset(dp,-0x3f,sizeof(dp));
    	dp[0]=0;
    	for(int i=1;i<=cnt;++i){
    		for(int j=i-1;~j;--j){
    			if(k[i].pos-k[j].pos<=17&&ok[k[i].pos-k[j].pos]) dp[i]=max(dp[i],dp[j]+k[i].val);
    			if(k[i].pos-k[j].pos>17){
    				dp[i]=max(dp[i],mx[j]+k[i].val);
    				break;
    			}
    		}
    		ans=max(ans,dp[i]);
    		mx[i]=max(mx[i-1],dp[i]);
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    

    B:小奇的矩阵(matrix)

    平均数这玩意显然可拆:令$sum=sum A_i$,$sqr=sum A_{i}^{2}$

    $Aavg=frac{sum}{n+m-1}$

    所以:原式=$(n+m-1)*sqr-sum^2$,推一下就能推出来

    然后。。。我竟然去想了搜索!

    正解dp:设$f[s][i][j]$表示到了点(i,j),总和为s时的最小sqr值

    所以f[s][i][j]=min(f[s-a[i][j]][i][j-1]+a[i][j]*a[i][j],f[s-a[i][j]][i-1][j]+a[i][j]*a[i][j]);

    然后就做出来了

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define inf 0x3f3f3f3f
    #define re register
    using namespace std;
    int t,n,m,a[35][35],ans,f[1805][35][35];
    signed main(){
    	scanf("%d",&t);
    	while(t--){
    		scanf("%d%d",&n,&m);
    		for(re int i=1;i<=n;i++)
    			for(re int j=1;j<=m;j++)
    				scanf("%d",&a[i][j]);
    		ans=inf;
    		memset(f,0x3f,sizeof(f));
    		f[a[1][1]][1][1]=a[1][1]*a[1][1];
    		for(re int i=1;i<=n;i++){
    			for(re int j=1;j<=m;j++)
    				for(re int k=1;k<=1800;k++){
    					if(k<a[i][j]) continue;
    					f[k][i][j]=min(f[k][i][j],f[k-a[i][j]][i-1][j]+a[i][j]*a[i][j]);
    					f[k][i][j]=min(f[k][i][j],f[k-a[i][j]][i][j-1]+a[i][j]*a[i][j]);
    				}
    		}
    		for(re int k=1;k<=1800;k++){
    			if(f[k][n][m]>=inf) continue;
    			ans=min(ans,(n+m-1)*f[k][n][m]-k*k);
    		}
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
    

    C:小奇的仓库(warehouse)

    美妙的换根dp

    设f[i][j]表示i到其他点,后4位状态为j的路径数

    f[x][0]=1

    f[x][(j+w)%16]+=f[y][j]

    f[y][(j+w)%16]+=(f[x][j]-f[y][((j-w)%16+16)%16])

    #include<iostream>
    #include<cstdio>
    #include<string>
    #include<cstring>
    #include<cmath>
    #define MAXN 100005
    #define int long long
    using namespace std;
    int n,m,ans=0;
    int to[MAXN<<1],nxt[MAXN<<1],pre[MAXN],cnt=0,w[MAXN<<1];
    void add(int u,int v,int val){
    	cnt++,to[cnt]=v,w[cnt]=val,nxt[cnt]=pre[u],pre[u]=cnt;
    }
    int g[MAXN],f[MAXN][17],t[MAXN][17];
    void DFS(int x,int fa){
    	f[x][0]=1;
    	for(int i=pre[x];i;i=nxt[i]){
    		int y=to[i];
    		if(y==fa) continue;
    		DFS(y,x);
    		g[x]+=g[y];
    		for(int j=0;j<16;j++){
                f[x][(j+w[i])&15]+=f[y][j];
    			g[x]+=f[y][j]*w[i];
    		}
    	}
    }
    void dfs(int x,int fa){
    	for(int i=pre[x];i;i=nxt[i]){
    		int y=to[i];
    		if(y==fa) continue;
    		int size=0;
    		for(int j=0;j<16;j++){
    			t[y][(j+w[i])&15]+=f[x][j]-f[y][((j-w[i])&15+16)&15];
    			size+=f[y][j];
    		}
    		g[y]=g[x]+(n-size*2)*w[i];
    		for(int j=0;j<16;j++) f[y][j]+=t[y][j];
    		dfs(y,x);
    	}
    }
    signed main(){
    	scanf("%lld%lld",&n,&m);
    	for(int i=1,u,v,val;i<n;i++){
    		scanf("%lld%lld%lld",&u,&v,&val);
    		add(u,v,val),add(v,u,val);
    	}
    	DFS(1,0);
    	dfs(1,0);
    	for(int i=1;i<=n;i++){
    		for(int j=0;j<16;j++){
                int k=j^m;
                g[i]+=(k-j)*f[i][j];
            }
            printf("%lld
    ",g[i]-m);
    	}
    	return 0;
    }
    
  • 相关阅读:
    易优CMS:compare的基础用法
    易优CMS:switch的基础用法
    2019年创意可爱卡通小清新教育课件培训PPT模板
    简约清新立体商务年终工作总结汇报动态PPT模板
    中国古风唯美水墨工作计划汇报PPT模板推荐
    新学期教育教学小学家长会PPT模板推荐
    遇见手绘花卉小清新简约通用PPT模板推荐
    简约清新日系你好五月通用PPT模板推荐
    清新简约风格毕业论文答辩PPT模板推荐
    C# 中=>符号的使用
  • 原文地址:https://www.cnblogs.com/Juve/p/11383455.html
Copyright © 2020-2023  润新知