P5322 排兵布阵
1.对于玩家a而言,攻占第i个城堡,需要至少派出2∗a[i]+1的兵力
如果攻占下一个玩家的城堡,那么出兵更弱的玩家自然也会被攻占
2.可以先对每个城堡的玩家兵力排序,于是就可以简化成:有i个城堡,每个城堡有s个玩家,攻占一个玩家需要派出2∗i[s]+1的兵力,获得i∗s的分数。求在有m个兵力的情况下,对于每个城堡要攻占哪一个玩家使得总分最大。
3.所以可以联想到分组背包,把每一个城堡看成一个组,每个玩家是一个组内的物品。物品的价值即为所获分数i∗s,体积为攻打所需兵力2∗i[s]+1,而背包体积就是兵力m。
代码也就很好写了:
#include <bits/stdc++.h>
using namespace std;
int s,n,m;
int C[110][110],W[110][110];
int F[20010];
void package(int V,int K,int GMC){
for(int k=1;k<=K;k++){
for(int v=V;v>0;v--){
for(int i=1;i<=GMC;i++){
if(v-C[k][i]>=0&&F[v-C[k][i]]+k*i>F[v]){
F[v]=F[v-C[k][i]]+k*i;
}
}
}
}
}
int main(){
cin>>s>>n>>m;
for(int i=1;i<=s;i++){
for(int j=1;j<=n;j++){
cin>>C[j][i];
}
}
for(int i=1;i<=n;++i){
sort(C[i]+1,C[i]+1+s);
for(int j=1;j<=s;j++){
C[i][j]=C[i][j]*2+1;
}
}
package(m,n,s);
cout<<F[m]<<endl;
return 0;
}
P6359 Cloud computing
1.按照f[i]从大到小排序,如果f[i]相等,则按照v[i] 从小到大排序(贪心),比较大的f[i]可以满足更多的需求,而当f[i]相等,自然要选择比较小的v[i]。
2.定义dp[i]表示使用i个内核可以获得的最大利润,而该题涉及两个操作,即买入和卖出,买入的价格为了和卖出统一,就变成负数。
注意排序要将 n+m个信息进行操作,用tot来存储当前的核心数,方便进行背包处理,处理是最基础的背包问题(01 背包)。
最后枚举0~tot,求dp最大值
代码如下:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=4010,M=2e5+10;
int n,m,dp[M],tot,ans;
struct node{
int c,f,v;
}a[N];
inline bool cmp(node x,node y){
return x.f==y.f?x.v<y.v:x.f>y.f;
}
inline int max(int x,int y){
return x>y?x:y;
}
inline int min(int x,int y){
return x<y?x:y;
}
signed main(){
memset(dp,128,sizeof(dp));
dp[0]=0;
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld %lld %lld",&a[i].c,&a[i].f,&a[i].v);
a[i].v=-a[i].v;
}
scanf("%lld",&m);
for(int i=n+1;i<=n+m;i++){
scanf("%lld %lld %lld",&a[i].c,&a[i].f,&a[i].v);
}
stable_sort(a+1,a+1+n+m,cmp);
for(int i=1;i<=n+m;i++){
if(a[i].v<0){
for(int j=tot;j+1;j--){
dp[j+a[i].c]=max(dp[j+a[i].c],dp[j]+a[i].v);
}
tot+=a[i].c;
}
else{
for(int j=0;j<=tot;j++){
dp[j]=max(dp[j],dp[j+a[i].c]+a[i].v);
}
}
}
for(int i=0;i<=tot;i++){
ans=max(ans,dp[i]);
}
printf("%lld\n",ans);
return 0;
}
P6280 Exercise G
1.可以发现如果循环了几次可以回到原地,当且仅当形成了一个环,假如设每个环的长度是 a[i],那么可以发现 k=lcm a[i],又知道 lcm 其实就是将那几个数分解质因数,然后取每个质数的最高次幂乘起来,所以可以枚举素数来做dp。
2.设 f(i,j)表示前 i 个素数总和为 j 的所有 k 的总和,枚举第 i 个素数的幂进行转移,因为之前并没有用过第 i 个素数,所以应把上一个状态乘上 p[i][k],所以直接有方程 f(i,j)=∑f(i−1,j−p[i][k])×p[i][k]
3.利用滚动数组压缩并倒叙枚举,f(0)=1,最后答案是 ∑f(i)
最后呈上代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e4+10;
bool vis[N];
vector<int> p;
ll f[N]={1},m;
int n;
int main(){
p.push_back(0);
cin>>n>>m;
for(int i=2;i<=n;i++){
if(!vis[i]) p.push_back(i);
for(int j=i*i;j<=n;j+=i){
vis[j]=1;
}
}
for(int i=1;i<p.size();i++){
for(int j=n;j>=p[i];j--){
int tmp=p[i];
while(tmp<=j){
f[j]=(f[j]+f[j-tmp]*tmp%m)%m;
tmp*=p[i];
}
}
}
ll ans=0;
for(int i=0;i<=n;i++){
ans=(ans+f[i])%m;
}
printf("%lld\n",ans);
return 0;
}