这一周继续做题,发现了自己很多的问题和不足需要改进。
P1064 [NOIP2006 提高组] 金明的预算方案
这是一道的背包题目,不过在原来完全背包的基础上增加了一些变化:每一件物品都有至多2个附件,只有加入该物品其附件才能加入。
刚开始有点糊涂,直接把附件当做一个物品,每次加入背包都判断一下,然后就直接超时。
后来看题解一想,其实可以每次判断主件是否需要加入背包时同时再判断加入0或者1或者2个附件是否会更好,这样既不会影响求背包又加快了速度。
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
struct piece
{
int x,y;
} buy[70][3];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
int a,b,c;
for(int j=1; j<=m; j++)
{
scanf("%d%d%d",&a,&b,&c);
if(c==0)
{
buy[j][0].x=a;
buy[j][0].y=b;
}
else if(buy[c][1].y==0)
{
buy[c][1].x=a;
buy[c][1].y=b;
}
else
{
buy[c][2].x=a;
buy[c][2].y=b;
}
}
int dp[40000]= {0};
for(int j=1; j<=m; j++)
for(int i=n; i>=buy[j][0].x; i--)
{
dp[i]=max(dp[i],dp[i-buy[j][0].x]+buy[j][0].x*buy[j][0].y);
if(i-buy[j][0].x-buy[j][1].x>=0)
dp[i]=max(dp[i],dp[i-buy[j][0].x-buy[j][1].x]+buy[j][0].x*buy[j][0].y+buy[j][1].x*buy[j][1].y);
if(i-buy[j][0].x-buy[j][2].x>=0)
dp[i]=max(dp[i],dp[i-buy[j][0].x-buy[j][2].x]+buy[j][0].x*buy[j][0].y+buy[j][2].x*buy[j][2].y);
if(i-buy[j][0].x-buy[j][1].x-buy[j][2].x>=0)
dp[i]=max(dp[i],dp[i-buy[j][0].x-buy[j][1].x-buy[j][2].x]+buy[j][0].x*buy[j][0].y+buy[j][1].x*buy[j][1].y+buy[j][2].x*buy[j][2].y);
}
printf("%d\n",dp[n]);
}
不过这里主件的序号不是每个主件在所有主件中的顺序,而是每个主件在所有件中的顺序,就因为这个,找了好久,第二天才搞清楚。
P1065 [NOIP2006 提高组] 作业调度方案
这是一道模拟题,由于不了解这种题,刚开始我还以为是一道贪心题,后来又以为是一道dp,最后看了看题解才知道还有这种类型的题,然后仔细读了读题目才豁然开朗。
#include <iostream>
#include <cstdio>
#include <map>
using namespace std;
int main()
{
int m,n;
scanf("%d%d",&m,&n);
int sx[401]={0},jqh[401]={0},sj[401]={0},x;
map<int,int>c;
for(int j=1;j<=n*m;j++)
{
scanf("%d",&x);
c[x]++;
sx[j]=(x-1)*m+c[x];
}
for(int j=1;j<=n*m;j++)
scanf("%d",&jqh[j]);
for(int j=1;j<=n*m;j++)
scanf("%d",&sj[j]);
int jq[21][8001]={0},fin[21]={0};
for(int j=1;j<=n*m;j++)
{
int gj=sx[j]/m;
int g=jqh[sx[j]];
int time=sj[sx[j]];
if(sx[j]%m)
gj++;
for(int i=fin[gj]+1;;i++)
{
if(jq[g][i]==0)
{
int pd=1;
for(int t=i;t<=i+time-1;t++)
if(jq[g][t]!=0)
pd=0;
if(pd==1)
{
for(int t=i;t<=i+time-1;t++)
jq[g][t]=1;
fin[gj]=i+time-1;
break;
}
}
}
}
int ans=0;
for(int j=1;j<=n;j++)
if(ans<fin[j])
ans=fin[j];
printf("%d\n",ans);
}
P1069 [NOIP2009 普及组] 细胞分裂
这是一道数学问题。给出一个底数和它的幂,然后要求从给出的数中找出经过最小的幂运算可以被其整除的数。这道题比较简单,只需要算出其中给出的底数的质因数在每个数中的个数然后判断一下就可以了。不过要注意时间复杂度的优化,不然会超时。
刚开始我直接把每一个数的质因数算了一遍,结果直接超时。
#include <iostream>
#include <cstdio>
#include <map>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <climits>
using namespace std;
int pdss(int x)
{
for(int j=2; j<=sqrt(x); j++)
if(x%j==0)
return 0;
return 1;
}
int main()
{
int n,m1,m2,s[10001]= {0};
map<int,int>pd;
scanf("%d%d%d",&n,&m1,&m2);
for(int j=1; j<=n; j++)
scanf("%d",&s[j]);
s[0]=m1;
if(m1==1)
{
printf("%d\n",0);
return 0;
}
for(int i=2; pdss(m1)!=1&&i<=sqrt(m1); i++)
while(m1%i==0)
{
pd[i]++;
m1/=i;
}
if(m1!=1)
pd[m1]++;
for(map<int,int>:: iterator it= pd.begin(); it!=pd.end(); it++)
it->second*=m2;
int haveans=0,sc[10001]={0};
for(int j=1; j<=n; j++)
{
int p=1;
for(map<int,int>:: iterator it= pd.begin(); it!=pd.end(); it++)
if(s[j]%it->first==0)
{
int js=0;
while(s[j]%it->first==0)
{
s[j]/=it->first;
js++;
}
int x=it->second/js;
if(it->second%js!=0)
x++;
sc[j]=max(x,sc[j]);
}
else
{
p=0;
break;
}
if(p==1)
haveans=1;
else
sc[j]=INT_MAX;
}
if(haveans==0)
printf("%d\n",-1);
else
{
sort(sc+1,sc+1+n);
printf("%d\n",sc[1]);
}
return 0;
}
P1072 [NOIP2009 提高组] Hankson 的趣味题
这道题我刚开始直接暴力查找,然后超时。后来知道可以先找出b1的各个因数,然后再在其中判断即可,减少了很多的多余运算。
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
#define ll long long
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int main()
{
int n;
ll a,b,c,d;
scanf("%d",&n);
while(n--)
{
scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
int ans=0;
for(ll j=1; j<=sqrt(d); j++)
{
if(d%j!=0)
continue;
if(j%b==0&&gcd(j,a)==b&&j*c/gcd(j,c)==d)
ans++;
if(d/j==j)
continue;
if(d/j%b==0&&gcd(d/j,a)==b&&d/j*c/gcd(d/j,c)==d)
ans++;
}
printf("%d\n",ans);
}
}
通过做这几道题,发现找bug和优化计算的能力需要提高,而且现在做题太慢,需要不断提高。