Stone game
题目链接
题意:有n个石头,让你选出来一堆,使得你选出来的这一堆的质量大于等于剩下的质量,并且将你选的这一堆中任意一个石子拿走,这一堆的质量就小于等于剩下的质量。
思路:刚刚学的退背包思想。将石子按从小到大排序,dp[k]表示取重量为k的方案数,这就是普通的0-1背包。对于a[i],我们将以a[i]为最小值并满足条件的方案加上。所以我们将选a[i]的情况减掉,然后再一层for判断如果满足条件,则将这种方案数加上。
#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=2e5+5;
const int mod=1e9+7;
int n,a[305];
LL dp[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
int sum=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
memset(dp,0,sizeof(dp));
dp[0]=1;
sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
{
for(int j=sum;j>=a[i];j--)
{
dp[j]=(dp[j]+dp[j-a[i]])%mod;
}
}
LL ans=0;
for(int i=1;i<=n;i++)
{
for(int j=a[i];j<=sum;j++)
{
dp[j]=(dp[j]-dp[j-a[i]]+mod)%mod;///将选a[i]的情况给减掉
}
for(int j=0;j<=sum;j++)
{
if(j+a[i]>=sum-j-a[i]&&j<=sum-j-a[i])ans=(ans+dp[j])%mod;
}
}
printf("%lld\n",ans);
}
}
D. Counting Sequences I
题目链接
题意:问长度为n的序列,满足序列的和等于乘积,的序列有多少种。
思路:暴力搜索,应用多重排列公式 n!/(k1!k2!…ki!),k为重复的数的个数,计算方案数。
#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=3005;
const int mod=1e9+7;
int ans[maxn],n;
LL f[maxn],inv[maxn];
LL qpow(LL a,LL b)
{
LL ans=1;
while(b)
{
if(b&1)ans=(ans*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return ans;
}
void init()
{
f[0]=1;
//inv[0]=1;
for(int i=1;i<=3000;i++)
{
f[i]=(f[i-1]*i)%mod;
}
for(int i=0;i<=3000;i++)inv[i]=qpow(f[i],mod-2);
}
LL dfs(int x,int num,int mul,int sum,int cnt,LL INV)///x是当前乘的数,num是不等于1的个数,mul是乘积的结果,sum是加和的结果,cnt是重复的数的个数
{
if(n==num)///如果n个数都用完了
{
if(mul==sum)return (((f[n]*inv[cnt])%mod)*INV)%mod;
return 0;
}
if(mul==sum+n-num)///如果当前满足条件,就将此方案加上去
{
return (((f[n]*INV)%mod)*((inv[cnt]*inv[n-num])%mod))%mod;
}
if(x>n||mul>sum+n-num||mul*x>sum+x+n-num-1)return 0;///剪枝
LL ans=0;
ans+=dfs(x+1,num,mul,sum,0,INV*inv[cnt]%mod)%mod;
ans+=dfs(x,num+1,mul*x,sum+x,cnt+1,INV)%mod;
return ans%mod;
}
int main()
{
init();
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
printf("%lld\n",dfs(2,0,1,0,0,1));
}
}