Spot
描述
有n个泥点,排成一排,第i个泥点坐标为ai。有m个木板,第i个木板长为li。现在用尽可能少的木板覆盖所有泥点。
问:使用木板的最少数量以及最优方案数(mod 1000000007),若不能完全覆盖,请输出“NO”。
注意:
泥点可重复覆盖,木板可重叠。
计算方案时,长度相等的两个板不等价,视为两种板。
输入
第1行:一个数n
第2行:n个数a1,a2…an(从小到大给出)
第3行:一个数m
第4行:m个数l1,l2…ln
输出
第1行,一个数,使用木板的最少数量
第2行,一个数,最优方案数
若不能完全覆盖,请输出“NO”
样例输入
1
0
1
10
样例输出
1
10
数据范围和约定
对于20%的数据:n=1
对于另外20%的数据:m=1
对于100%的数据:
1<=n,m<=15
0<=ai<=1000000000
1<=li<=1000000000
时空限定
内存限制为 512 MB
时间限制为 1 s
评测环境和细则
评测开启-O2优化
评测软件为lemon
评测忽略行尾空格
文件名
提交文件名为spot.cpp/pas
输入文件名为spot.in
输出文件名为spot.out
n,m<=15,先确定是状压
一开始f[i][j],i表示前i个木板,j压的是泥点
再用g[i][j] 在dp的时候记录方案数
但是要考虑的情况会多到爆炸
在卡了一个下午后,本蒟蒻毅然决然的决定 换!状!态!!!!
用f[i][j],i表示前i个泥点,j压的是木板,f[i][j]表示方案数
初始化成负数
最后再扫一遍f[n][0-maxp],找不是负数中最小的,没找着就输出”NO”
这样比以前的好处是:
泥点最后一定要全都选完,但木板不一定,比较好转移。
错误的第一次code
#include<cstdio>
#include<cstring>
#include<iostream>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
const int mod=1000000007;
const int N=1<<15;
ll minn(ll a,ll b){return a<b?a:b;}
ll maxn(ll a,ll b){return a>b?a:b;}
int n,m,maxp;
int pos[21],len[21];
int f[16][N+10];
ll g[16][N+10];
ll num;
ll ans;
void out11()
{
printf("\n\n");
for(int i=1;i<=m;++i)
{
for(int j=0;j<=maxp;++j)
printf("%d ",f[i][j]);
printf("\n");
}
printf("\n");
for(int i=0;i<=m;++i)
{
for(int j=0;j<=maxp;++j)
printf("%lld ",g[i][j]);
printf("\n");
}
printf("\n");
}
int main(){
//freopen("1.txt","r",stdin);
freopen("spot6.in","r",stdin);
//freopen("spot5.in","r",stdin);
//freopen("spot.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&pos[i]);
scanf("%d",&m);
for(int i=1;i<=m;++i)
scanf("%d",&len[i]);
// n为泥点个数 m为木板个数
maxp=(1<<n)-1;
mem(f,0x7f);
f[0][0]=0;
g[0][0]=1;
int r,temp,l1,r1,l2,r2,judge1;
ll llnum,lrnum,rrnum,rlnum,fangan;
ll temp1,temp2;
int qian,now;
for(int i=1;i<=m;++i)
for(int j=0;j<=maxp;++j)
{
if(f[i][j]==f[i-1][j]){g[i][j]=(g[i-1][j]+g[i][j])%mod;}
else if(f[i][j]>f[i-1][j]){f[i][j]=f[i-1][j];g[i][j]=g[i-1][j];}
qian=maxp+1;
for(int l=1;l<=n;++l)
{
r=l;temp=j;
while(pos[r]-pos[l]+1<=len[i]&&r<=n)++r;--r;
now=0;
for(int p=l;p<=r;++p)
if(temp&(1<<(p-1)))
now|=(1<<(p-1));
if(now==qian)continue;
qian=now;
judge1=0;
for(int p=l;p<=r;++p)
{
l2=p;
if(temp&(1<<(p-1)))
{
judge1=1;
break;
}
}
for(int p=r;p>=l;--p)
{
r2=p;
if(temp&(1<<(p-1)))
break;
}
if(!judge1)continue;//选的区间中 状态都没有
l1=l2-1;
while(l1>=1&& !((1<<(l1-1))&temp) )--l1;
r1=r2+1;
while(r1<=n&& !((1<<(r1-1))&temp) )++r1;
llnum=(l1==0?-0x7fffffff:pos[l1]+1);
lrnum=pos[r2]-len[i]+1;
rlnum=pos[l2]+len[i]-1;
rrnum=(r1==n+1?0x7fffffff:pos[r1]-1);
temp1=minn(rlnum,rrnum)-pos[r2]+1;
temp2=pos[l2]-maxn(llnum,lrnum)+1;
//printf("temp1=%lld temp2=%lld\n",temp1,temp2);
fangan=(minn(temp1,temp2))%mod;
//if(fangan<0)
// cout<<fangan<<endl;
//printf("i=%d j=%d l=%d r=%d lnum=%lld rmun=%lld fangan=%lld\n",i,j,l,r,lnum,rnum,fangan);
for(int p=l;p<=r;++p)
temp=temp&(~(1<<(p-1)));
if(f[i][j]==f[i-1][temp]+1)
g[i][j]=(g[i][j]+g[i-1][temp]*fangan%mod)%mod;
else
if(f[i][j]>f[i-1][temp]+1)
{
f[i][j]=f[i-1][temp]+1;
g[i][j]=g[i-1][temp]*fangan%mod;
}
}
}
if(f[m][maxp]==f[0][maxp])
{
printf("NO");
//while(1);
return 0;
}
printf("%d\n",f[m][maxp]);
for(int i=1;i<=m;++i)
if(f[i][maxp]==f[m][maxp])
ans=(ans+g[i][maxp])%mod;
printf("%lld",ans);
//可以用dfs???
//dfs(int x,int now)
//外面一个judge数组或二进制变量记录泥点选没选
//x:第几块木板
//now:以前用的木板数
//可能左右挪比较恶心吧!!!
/*judge=0;
dfs(1,0,1);*/
//out11();
while(1);
return 0;
}
正确的大神第一次想的那样的标程:
AC我的code: