Loading

noip模拟30

A.毛一琛

指数级枚举,考虑\(Meet\ in\ the\ middle\),这是一个非常优的策略..
然而考场上想到了,但是不会用..
而这道题使用了一个高超的STL剪枝,就是用\(map\)对与已经考虑过的状态进行去重..
然而我不会..

A_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
	#define ll long long int 
	#define ull unsigend ll
	#define re register ll 
	#define lf double
	#define mp(x,y) make_pair(x,y)
	#define lb lower_bound 
	#define ub upper_bound
	#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
	#define Fill(x,y) memset(x,y,sizeof x)
	#define Copy(x,y) memset(x,y,sizeof x)
	inline ll read() {
		ll ss=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0; 
		while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
		return cit?ss:-ss;
	}
} using namespace BSS;

ll n,mid,rest,tot,ans;
ll w[30],vis[1<<21];
map<ll,ll> maps;
vector<ll> vec[1<<21];
void dfs1(ll now,ll sta,ll res){
	if(now==mid+1){
		if(maps.find(res)==maps.end()) maps[res]=++tot;
		vec[maps[res]].push_back(sta);
		return ;
	}
	dfs1(now+1,sta,res);
	dfs1(now+1,sta|(1<<(now-1)),res-w[now]);
	dfs1(now+1,sta|(1<<(now-1)),res+w[now]);
}
void dfs2(ll now,ll sta,ll res){
	if(now==rest+1){
		if(maps.find(res)==maps.end()) return ;
		for(auto i : vec[maps[res]]){
			vis[(sta<<mid)|i]=1;
		}
		return ;
	}
	dfs2(now+1,sta,res);
	dfs2(now+1,sta|(1<<(now-1)),res-w[now+mid]);
	dfs2(now+1,sta|(1<<(now-1)),res+w[now+mid]);
}
signed main(){
	n=read(); mid=n>>1; rest=n-mid;
	for(re i=1;i<=n;i++) w[i]=read();
	dfs1(1,0,0);  dfs2(1,0,0);
	for(re i=1;i<=(1<<n)-1;i++) ans+=vis[i];
	printf("%lld",ans);
	return 0;
}

B.毛二琛

又是一道想半天也想不出来的Dp..
自己想的是打表找规律,也考虑过了Dp,但是都失败了..
正解不是找规律,而是考虑如何转移到目标状态,转移的过程中记录方案数量..
针对每个位置交换的先后顺序..然后排除不合法的方案,考虑合法的方案即可..
这个可以自己暴力手模找规律,也可以自己动脑想..然而我只能手模之后动脑想..
代码注释部分为\(O(n^3)\),前缀和优化成\(O(n^2)\)..

B_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
	#define ll long long int 
	#define ull unsigend ll
	#define re register ll 
	#define lf double
	#define mp(x,y) make_pair(x,y)
	#define lb lower_bound 
	#define ub upper_bound
	#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
	#define Fill(x,y) memset(x,y,sizeof x)
	#define Copy(x,y) memset(x,y,sizeof x)
	inline ll read() {
		ll ss=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0; 
		while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
		return cit?ss:-ss;
	}
} using namespace BSS;

const ll N=5e3+50;
const ll mod=1e9+7;

ll n;
ll p[N],pre[N],dir[N]; // dir 记录标号,1为小于号
ll dp[N][N];
// 1. 逆序对个数等于n时有解 2. p[i]==i 时无解
inline void Work(){
	for(re i=1;i<=n;i++){
		if(p[i]==i){ puts("0"); return ; }
		if(p[i]<i){
			if(dir[i]&1 and i!=1) { puts("0"); return ; }
			dir[i]=2;
			for(re j=i-1;j>=p[i]+1;j--){
				if(dir[j]&2) { puts("0"); return ; }
				dir[j]=1;
			}
			dir[p[i]]=2;
		}
		if(p[i]>i){
			for(re j=i+1;j<=p[i]-1;j++){
				if(dir[j]&1) { puts("0"); return ; }
				dir[j]=2;
			}
 		}
	}
	dp[1][1]=1;
	for(re i=1;i<=n;i++) pre[i]=pre[i-1]+dp[1][i];
/*	for(re i=2;i<=n-1;i++)
	{
		for(re j=1;j<=i;j++)
		{
			if(dir[i]&1)
			for(re k=j;k<=i;k++)
			{
				dp[i][j]+=dp[i-1][k];
			}
			else
			for(re k=0;k<=j-1;k++)
			{
				dp[i][j]+=dp[i-1][k];
			}
			cout<<"dp:"<<dp[i][j]<<" ";
		}
	}
*/	for(re i=2;i<=n-1;i++){
		for(re j=1;j<=i;j++){
			if(dir[i]&1){
				dp[i][j]=(pre[i]-pre[j-1]+mod)%mod;
			}
			else{
				dp[i][j]=pre[j-1];
			}
		}
		for(re j=1;j<=n;j++){
			pre[j]=(pre[j-1]+dp[i][j])%mod;
		}
	}
	printf("%lld",pre[n-1]%mod);
	return ;
}
signed main(){
	n=read();
	for(re i=1;i<=n;i++){
		p[i]=read()+1;
	}
	Work();
	return 0;
}

C.毛三琛

一道随机化算法..
正解是二分答案+剪枝..
可以发现背包体积越大,那么需要的个数越少..所以是单调的..
随机打乱枚举顺序,然后二分背包体积,如果当前的\(ans\)代入二分的背包体积后发现更劣,说明答案一定不是这个,从而剪枝..

C_code
#include<bits/stdc++.h>
using namespace std;
namespace BSS {
	#define ll long long int 
	#define ull unsigend ll
	#define re register ll 
	#define lf double
	#define mp(x,y) make_pair(x,y)
	#define lb lower_bound 
	#define ub upper_bound
	#define File(x,y) freopen(#x,"r",stdin),freopen(#y,"w",stdout)
	#define Fill(x,y) memset(x,y,sizeof x)
	#define Copy(x,y) memset(x,y,sizeof x)
	inline ll read() {
		ll ss=0; bool cit=1; char ch;
		while(!isdigit(ch=getchar())) if(ch=='-') cit=0; 
		while(isdigit(ch)) ss=(ss<<3)+(ss<<1)+(ch^48),ch=getchar();
		return cit?ss:-ss;
	}
} using namespace BSS;

const ll N=2e4+50;
ll m,n,ans,tot,mod;
ll v[N],w[N],rs[N];
inline bool check(ll x)
{
//	cout<<"x:"<<x<<"\t";
	ll sum=1,temp=x;
	for(re i=1;i<=n;i++)
	{
		if(x<w[i]) return 0;
		if(temp>=w[i]) temp-=w[i];
		else temp=x-w[i],sum++;
	}
//	cout<<"sum:"<<sum<<"\n";
	return sum<=m;
}
inline void Work(ll x)
{
	for(re i=1;i<=n;i++) w[i]=(v[i]+x)%mod;
//	for(re i=1;i<=n;i++) cout<<w[i]<<" ";
	if(!check(ans)) return ;
	ll le=0,ri=tot+1,mid;	
	while(le<=ri)
	{
		mid=(le+ri)>>1;
		if(check(mid)) ri=mid-1;
		else le=mid+1;
	}
	ans=min(ans,le);
	return ;
}	
signed main()
{
	ll t1=clock(); srand(time(0));
	n=read(); mod=read(); m=read();
	for(re i=1;i<=n;i++) v[i]=read(),tot+=v[i];
	ans=tot;
	for(re i=0;i<=mod;i++) rs[i+1]=i;
	random_shuffle(rs+1,rs+1+mod);
	ll i=1;
	while(clock()-t1<=980000 and i<=mod)
	{
		Work(rs[i++]);
	}
	printf("%lld",ans);
	return 0;
}
posted @ 2021-08-09 06:07  AaMuXiiiiii  阅读(55)  评论(0编辑  收藏  举报