比赛链接:https://www.nowcoder.com/acm/contest/141#question
A | PACM Team |
题意:有N个专家团队,第i个团队给出pi,ai,ci,mi,gi分别代表p、a、c、m类型专家的数量以及gi代表团队知识点。要求选取某些专家团队使得p、a、c、m类型的专家总人数不超过P、A、C、M个,而且尽量使得gi之和大。
解析:方法一:直接DFS+剪枝,用时160ms。方法二:标程解法,DP。dp[i][p][a][c][m]表示前i个团队中选取某些团队而且专家的数量不超过p、a、c、m的最多知识点数。状态转移如下:dp[i+1][ip][ia][ic][im]=dp[i][ip-p[i]][ia-a[i]][ic-c[i]][im-m[i]]+g[i];
队友代码(DFS):
#include<bits/stdc++.h>
using namespace std;
int n,sum[55],look[55],P,A,M,C,ans,pp[55],x,a[10];
struct AA
{
int p,a,c,m,g;
}pos[50];
int dfs(int num,int all,int z)
{
if(num>n)
{
if(all>ans)
{
ans=all;
x=z;
for(int i=1;i<=n;i++)
pp[i]=look[i];
}
return 0;
}
if(sum[n]-sum[num-1]+all<=ans) return 0;//剪枝,如果加上剩下的所有都不如已有答案大,那么就不用往下找了。
if(pos[num].p+a[1]<=P&&pos[num].a+a[2]<=A&&pos[num].c+a[3]<=C&&pos[num].m+a[4]<=M)
{
look[num]=1;
a[1]+=pos[num].p;
a[2]+=pos[num].a;
a[3]+=pos[num].c;
a[4]+=pos[num].m;
dfs(num+1,all+pos[num].g,z+1);
a[1]-=pos[num].p;
a[2]-=pos[num].a;
a[3]-=pos[num].c;
a[4]-=pos[num].m;
look[num]=0;
}
if(sum[n]-sum[num]+all<=ans) return 0;
dfs(num+1,all,z);
return 0;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d%d%d%d",&pos[i].p,&pos[i].a,&pos[i].c,&pos[i].m,&pos[i].g);
sum[i]=sum[i-1]+pos[i].g;
}
scanf("%d%d%d%d",&P,&A,&C,&M);
dfs(1,0,0);
printf("%d\n",x);
for(int i=1;i<=n;i++)
{
if(pp[i]) printf("%d ",i-1);
}
printf("\n");
}
代码(DP):
#include <bits/stdc++.h>
using namespace std;
const int N=37;
int n,p[N],a[N],c[N],m[N],g[N];
int P,A,C,M;
short dp[N][N][N][N][N];//注意用sort不会爆内存
bool tk[N][N][N][N][N];
int main()
{
cin>>n;
for(int i=0; i<n; i++)
cin>>p[i]>>a[i]>>c[i]>>m[i]>>g[i];
cin>>P>>A>>C>>M;
for(int i=0; i<=n; i++)
for(int ip=0; ip<=P; ip++)
for(int ia=0; ia<=A; ia++)
for(int ic=0; ic<=C; ic++)
for(int im=0; im<=M; im++)
tk[i][ip][ia][ic][im]=false;
for(int i=0; i<n; i++)
{
for(int ip=P; ip>=0; ip--)
for(int ia=A; ia>=0; ia--)
for(int ic=C; ic>=0; ic--)
for(int im=M; im>=0; im--)
dp[i+1][ip][ia][ic][im]=dp[i][ip][ia][ic][im];
for(int ip=P; ip>=p[i]; ip--)
for(int ia=A; ia>=a[i]; ia--)
for(int ic=C; ic>=c[i]; ic--)
for(int im=M; im>=m[i]; im--)
if(dp[i][ip-p[i]][ia-a[i]][ic-c[i]][im-m[i]]+g[i]>dp[i+1][ip][ia][ic][im])
{
dp[i+1][ip][ia][ic][im]=dp[i][ip-p[i]][ia-a[i]][ic-c[i]][im-m[i]]+g[i];
tk[i+1][ip][ia][ic][im]=true;//选中i来组合答案
}
}
//fprintf(stderr, "ans=%d\n", dp[n][P][A][C][M]);
vector<int> ans;
for(int i=n; i>=1; i--)
{
if(tk[i][P][A][C][M])
{
ans.push_back(i-1);
P-=p[i-1];
A-=a[i-1];
C-=c[i-1];
M-=m[i-1];
}
}
reverse(ans.begin(), ans.end());
printf("%d\n", (int)ans.size());
for(size_t i=0; i<ans.size(); i++)
printf("%d%c", ans[i], " \n"[i+1==ans.size()]);
return 0;
}
C | Shuffle Cards |
题意:初始有长为N的串1~N,对其进行M次操作,每次操作给出L和R,作用是将串中的第L到第L+R-1个字符剪下放到数组前面。
解析:是平衡数的模板题,题目可以用封装好的<rope>来写。
代码:
#include<bits/stdc++.h>
#include <ext/rope>
using namespace std;
using namespace __gnu_cxx;
rope<int>s;
int main()
{
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
s.push_back(i);
int a,b;
for(int i=0;i<m;i++)
{
scanf("%d %d",&a,&b);
s=s.substr(a-1,b)+s.substr(0,a-1)+s.substr(a+b-1,n-a-b+1);
}
for(int i=0;i<n-1;i++)
printf("%d ",s[i]);
printf("%d",s[n-1]);
}
H | Diff-prime Pairs |
题意:给出N,求有多少对(i,j),满足i/gcd(i,j)与j/gcd(i,j)都是素数。注意如果 i1 ≠ i2 或 j1 ≠ j2则 (i1, j1) 与 (i2, j2) 是不同的。
解析:首先筛出N之内的素数。若i/gcd(i,j)是素数,那么i,j就是素数的gcd(i,j)倍,想到我们可以从1~N枚举所有gcd(i,j),那么我们可以二分找到最大的素数maxP满足maxP*gcd(i,j)<=N,由于我们二分时就知道了maxN是第几个数素数(假设为num),那么 (i/gcd(i,j),j/gcd(i,j))就是由前num个素数组合而成,方案数为num*(num-1),每种方案与一种(i,j)一一对应。
代码:
#include<bits/stdc++.h>
using namespace std;
#define M 10000000
int prime[M+5];
long long p;
bool vis[M+5];
long long ans;
int num;
void AA()
{
int i,j;
num=0;
for(i=2;i<M;i++)
{
if(!vis[i])
prime[num++]=i;
for(j=0;j<num;j++)
{
if(i*prime[j]>M)
break;
vis[i*prime[j]]=true;
if(i%prime[j]==0)
break;
}
}
}
int erfen(int x)
{
int l=0,r=p+1,mid=-1;
while(1)
{
if(mid==(l+r)/2)break;
else
{
mid=(l+r)/2;
if(prime[mid]<=x) l=mid;
else r=mid;
}
}
return l;
}
int main()
{
int n;
AA();
p=num-1;prime[num]=M;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
p=erfen(n/i);
ans+=p*(p+1);
}
printf("%lld\n",ans);
return 0;
}