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;
}