• 2020小米邀请赛决赛补题G,I,J(三DP)


    1:G.Rikka with Game Theory
    题意:给一个n阶无向图(n<=17),要求你给每个点赋值,使得每个点的值都是与之连接的点的值的mex,求赋值的方案数。
    题解:看范围识状压,主要转移和状态是重点,
    假设集合V0为赋0的集合,可以推出两个重要条件:
    1:显然该集合的所有点不能相连,否则就会有矛盾
    2:其他值大于0的点均与该集合里面的点有边存在,否则本身的值就是0.
    假设V1为赋1的集合,同样可以推出两个条件
    1:本身点不能相连
    2:其他值大于1的点均与该集合有边存在
    .
    .
    .
    以此类推,就相当于在图上去掉一个个覆盖独立集,直到取完有多少方案。
    定义dp[state]代表图为state的时候有多少种方案,枚举state的覆盖独立子图son,则dp[i]+=dp[i^j];

    #include<bits/stdc++.h>
    using namespace std;
    #define f(i,a,b) for(int i=a;i<=b;++i)
    #define ll long long
    #define N 200005
    inline ll read(){ll x;scanf("%lld",&x);return x;}
    ll eg[N],dp[N],w[N],n,m;
    int main()
    {
    	dp[0]=1;
    	n=read(),m=read();
    	f(i,1,m)
    	{
    		int x=read()-1,y=read()-1;
    		eg[x]|=(1<<y);
    		eg[y]|=(1<<x);
    	}
    	ll tot=(1<<n)-1;
    	f(i,1,tot) f(j,0,n-1) if(i&(1<<j)) w[i]|=eg[j];
    	for(int i=1;i<=tot;++i)
    		for(int j=i;j;j=(j-1)&i)
    			if(((w[j]&j)==0) && ((w[j]&(i^j))==(i^j))) dp[i]+=dp[i^j];
    	cout<<dp[tot];
    	return 0;
    } 
    

    2:I.Rikka with RCPC
    题意:太长了。
    题解:显然,我们要尽量避免angry值达到T,因为这样显然在达到T之前就讲题来的更优惠,剩下的节省代价的方案就只剩下条件3了,这道题就转化成去掉所有满足条件3的子串,剩下不能去掉的直接算在答案的贡献上。
    定义dp[i]代表到i的最小代价,如果以i结尾不能找到何小于T的子串,就dp[i]=dp[i-1]+a[i];
    否则,dp[i]=min(dp[x])(其中x满足pre[i]-pre[x]<=T&&x<=i-k-1);
    min(dp[x])用单调队列优化或者线段树维护即可,(用线段树简单多了)
    注意数组的最后几个和小于T的数是可以省下的,但是省下不一定答案最优,所以在那几个数附近取dp最小值就行。

    #include<cstdio>
    #include<iostream>
    #define rep(i,s,t) for(int i=s;i<=t;++i)
    #define db double
    using namespace std;
    #define ll long long
    const int N=1e6+11;
    int n,k,T;
    int a[N],q[N];
    ll f[N],s[N],ans;
    int main(){
    	int l,r;
    	scanf("%d%d%d",&n,&k,&T);
    	++k;
    	rep(i,1,n)scanf("%d",a+i),s[i]=s[i-1]+a[i];
    	l=r=0;
    	rep(i,1,n){
    		f[i]=f[i-1];
    		if(i>=k){
    			while(l!=r&&f[q[r-1]]-s[q[r-1]]<f[i-k]-s[i-k])--r;
    			q[r++]=i-k;
    		}
    		while(l!=r&&s[q[l]]+T<s[i])++l;
    		if(l!=r)f[i]=max(f[i],f[q[l]]-s[q[l]]+s[i]);
    	}
    	ans=s[n];
    	rep(i,0,n)
    		if(s[n]-s[i]<=T)
    			ans=min(ans,s[i]-f[i]);
    	cout<<ans<<endl;
    	return 0;
    }
    

    3:J.Rikka with Book
    题意:n本书每本都有重量w[i]和长度l[i],都可以看成二维平面上的一个1l[i]的矩阵,现在要将这些书堆放起来,问在不失去平衡的条件下,书堆的边缘在水平方向上离重心的最远距离。
    题解:定义dp[state][i]代表状态为state,底部为i的堆法得到的离重心的最远距离,这样转移就很显然了,枚举底座,设质量m1,然后对于一堆书(质量m2)放在底座上,显然这堆书的中心应该处于底座的边缘,根据常识得,新的重心应该是离底座重心距离为m2/(m1+m2)
    l[底]/2的点,就可以转移了。
    但是要注意转移后的dp值不一定是放书的那一端,也可能是另一端。

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define f(i,a,b) for(register int i=a;i<=b;++i)
    #define ll long long
    #define N 2000005
    inline int read(){int x;scanf("%d",&x);return x;}
    double dp[N][21],Max[N],W[N],l[23],w[23],Sum[N];
    int n;
    void counts(int state)
    {
    	double ans=0;
    	for(int i=0;i<n;++i)
    		if(state&(1<<i)) Sum[state]++,ans+=w[i];
    	W[state]=ans; 
    	//cout<<state<<" "<<W[state]<<endl;
    }
    int main()
    {
    	n=read();
    	f(i,0,n-1) cin>>l[i];        //dp[i]=max(max((dp[old]*dpw[old]+w[j]*(dp[old]+l[j]/2))/dpw[i],dp[i]),((dpw[old]*l[j]+w[j]*l[j]/2)/dpw[i]));
    	f(i,0,n-1) cin>>w[i];
    	int tot=(1<<n)-1;
    	f(i,1,tot) counts(i);
    	f(i,1,tot) f(j,0,n-1) if(i&(1<<j))
    	{
    		if(Sum[i]==1)
    		{
    			Max[i]=l[j]/2;
    			continue;
    		}
    		double We=W[i^(1<<j)];
    		double len=(double)l[j]/2.0*(We/W[i]);
    		dp[i][j]=max(Max[i^(1<<j)]+l[j]/2.0-len,l[j]/2.0+len);
    		Max[i]=max(Max[i],dp[i][j]);
    	}
    	printf("%.10lf",Max[tot]);
    	return 0;
    }
    
    
  • 相关阅读:
    Unity3D GUI图形用户界面系统
    Unity3D 自动寻路入门指南
    Unity3D 导航网格自动寻路(Navigation Mesh)
    拓展通用的冒泡排序方法
    DoTween 应用设置
    DoTween 教程
    Unity3D 脚本手册
    unity3d中获得物体的size
    Unity自动寻路Navmesh之高级
    C# 代码页获取input的值
  • 原文地址:https://www.cnblogs.com/Tiork/p/14312403.html
Copyright © 2020-2023  润新知