T1谜题(nazo)
Problem Here
Solution
- 先把所有点的开关状态压成一个二进制数
- 按一个按钮就相当于异或上一个二进制数
- 显然不会按同一个按钮两次
- 枚举(leftlfloorfrac{n}{2} ight floor)个按钮每个按不按,计算只按前(leftlfloorfrac{n}{2} ight floor)个按钮得到每种状态的最少次数保存在map里
- 接着枚举后$ leftlceil frac{n}{2} ight ceil $个按钮每个按不按,根据按的按钮的异或和到map里找到对应状态并更新答案
用hash表优化map,时间复杂度可优化到(O( 2^{frac{n}{2}}))。
#include<bits/stdc++.h>
using namespace std;
inline long long read(){
long long x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
#define MN 45
#define M 6233333
int n,m,ans=40;
long long t,res,opt[MN];
struct Map{
struct edge{int nex,w;long long val;}e[M];int hr[M],cnt;
inline int&operator[](long long x){
register int i;
for(i=hr[x%M];i;i=e[i].nex)
if(e[i].val==x) return e[i].w;
e[++cnt]=(edge){hr[x%M],0,x};hr[x%M]=cnt;
return e[cnt].w;
}
}mp;
int main(){
// freopen("nazo.in","r",stdin);
// freopen("nazo.out","w",stdout);
n=read();m=read();
register int i,j,x,y,step;
for(i=0;i<n;i++) res^=((read()==0)*1ll)<<i,opt[i]|=1ll<<i;
for(i=1;i<=m;i++) x=read()-1,y=read()-1,opt[x]|=1ll<<y,opt[y]|=1ll<<x;
for(i=0;i<1<<n/2;i++){
for(t=res,j=step=0;j<n/2;j++) if((i>>j)&1) t^=opt[j],step++;
mp[t]=std::min(mp[t]?mp[t]:MN,step+1);
}
for(i=0;i<1<<n-n/2;i++){
for(t=j=step=0;j<n-n/2;j++) if((i>>j)&1) t^=opt[n-j-1],step++;
mp[t]?ans=std::min(ans,mp[t]+step-1):0;
}
return 0*printf("%d",ans);
}
T2染色(iro)
Problem Here
Solution
假设最后用了(m)种颜色,我们把这些颜色编号为(1)~(m)。
(m)显然存在一个下界,即每两个相邻节点的颜色数之和的最大值。
- 当(n)为偶数时,答案就是这个最大值。
如果您直接输出它,您会获得40分 - 当n为奇数时,假设1号点的颜色为(1)~(a_i),那么我要使偶数点所取得颜色尽可能小,奇数点所取得颜色尽可能大,显然可以通过贪心来完成。
所以,二分答案加+贪心check,时间复杂度(O(nlog n))
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
#define MN 100005
#define mid (l+r>>1)
int n,a[MN],MIN;
inline bool check(int md){
int x=a[2];
for(int i=3;i<=n;i++){
if(i&1){
x=std::max(0,a[i]-md+a[1]+x);
}
else{
x=std::max(0,a[i]-a[1]+x);
}
if(x==0) return 1;
}
return 0;
}
int main(){
n=read();
register int i;
for(i=1;i<=n;i++) a[i]=read(),MIN=std::max(MIN,a[i]+a[i-1]);
MIN=std::max(MIN,a[1]+a[n]);
if(~n&1) return 0*printf("%d
",MIN);
int l=MIN,r=MIN<<1,ans;
while(l<=r){
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("%d
",ans);
return 0;
}
T201串(kushi)
Problem Here
Solution
首先有一个很好想得n方dp。——70分
注意到我们只关心后缀,所以可以用后缀来表示状态
(dp_{i,j})表示前i个01串,其中一个子序列的末尾存在一个后缀等于j的最小答案。
(dp_{i,j} o dp_{i+1,j})有两种转移:
-
只要加上(g(a_i,a_{i+1})),可以直接加上这个值,若不执行该转移,将该值减去
-
枚举(a_{i+1})的一个前缀p,求出(min{dp_{i,p}}),再枚举(a_i)的后缀(u),用(min{dp_{i,p}})来更新(dp_{i+1,u})
复杂度是(O(nm)).
//70分dp
#include<bits/stdc++.h>
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
inline int read01(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
#define MN 1005
int ans,n,m,a[MN];
int cr[MN][MN],f[MN][MN];
inline void rw(int &i,int j){
if(i<j) i=j;
}
int main(){
freopen("kushi.in","r",stdin);
freopen("kushi.out","w",stdout);
n=read(),m=read();register int k,i,j;
if(n>1000) return 0*puts("orz");
for(i=1;i<=n;i++) a[i]=read01();
for(i=1;i<=n;i++)for(j=i+1;j<=n;j++)for(k=m;k>=0;k--)if((a[j]>>(m-k))==(a[i]%(1<<k))){cr[i][j]=k;break;}
for(i=0;i<=n;i++)for(j=0;j<=n;j++)if(i!=j){
if(i<j) rw(f[i][j+1],f[i][j]+cr[j][j+1]),
rw(f[j+1][j],f[i][j]+cr[i][j+1]);
else rw(f[i+1][j],f[i][j]+cr[i][i+1]),
rw(f[i][i+1],f[i][j]+cr[j][i+1]);
}
ans=0;
for(i=0;i<n;i++) rw(ans,f[i][n]),rw(ans,f[n][i]);
printf("%d
",m*n-ans);
return 0;
}
#include<bits/stdc++.h>
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
#define MN 200005
#define inf (1e9)
char s[25];
int a[MN],f[25][1<<20],cr,res;
int main(){
// freopen("kushi.in","r",stdin);
// freopen("kushi.out","w",stdout);
int n,m,i,j,k;
n=read();m=read();
for(i=1;i<=n;i++){
scanf("%s",s+1);
for(j=1;j<=m;j++) a[i]+=(s[j]=='0')<<j-1;
}
memset(f,67,sizeof f);
f[0][0]=m;
for(i=2;i<=n;i++){
for(k=0;k<m;k++)
if((a[i-1]>>k)==(a[i]&((1<<m-k)-1))) break;
cr+=k;res=inf;
for(j=0;j<=m;j++) res=std::min(res,f[m-j][a[i]&((1<<m-j)-1)]+j-k);
for(j=0;j<=m;j++) f[m-j][a[i-1]>>j]=std::min(f[m-j][a[i-1]>>j],res);
}
res=inf;
for(i=0;i<=m;i++)
for(j=0;j<1<<i;j++) res=std::min(res,f[i][j]);
printf("%d",res+cr);
}
Blog来自PaperCloud,未经允许,请勿转载,TKS!