拆项链珠子(背包变式)
Problem - G - Codeforces :G. Chevonne’s Necklace
题意:
有一个n颗珠子的项链,每颗珠子上都有个数字记作数组a[],每次你可以选择一颗珠子然后都会自左往右拆下a[i]-1个珠子;求可以拆下最大数量,以及多少种拆法;
思路:
该题主要是理解题目意思,有两大难点:
1.从不同的位置开始是否拆下珠子是否是一样的,其实都是一样的,无论从何处开始进行拆珠子,一种拆法从不同的地方进行拆解是相同的拆解方法;
2.无法将题意进行转化,其实题目求最大的数量就算是相连的位置进行拆除都并不会对其他珠子有影响。因为他并没有限定拆解的顺序,可以先拆解连续的珠子从右边往左边拆解,从而对选中的拆除的珠子完全没有任何影响;
在搞清这两个难点后,完全可以将题目意思进行转化,就是求所有数在进行求和中产生最大的值,而这个值不能超过n,多少种拆法就是多少种情况的求和为这个最大的数;
思路:就是通过求解背包问题的方式进行求解,枚举所有的珠子,每次去更新每颗珠子是否被移除,以及更新移除i课珠子的方案数量,就是先将0颗珠子的的移除情况和数量初始化为一,然后在枚举到这颗珠子的时候,进行向前寻找进行更新,通过之前的状态来进行更新;
更新珍珠状态:vis[j]|=vis[j-a[i]];
更新方案数量:dp[j]=(dp[j]+dp[j-a[i]])%mod;
代码如下:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
ll a[200001];
ll dp[20001]; // dp[i]表示移除i颗珍珠的方案数
ll vis[20001]; // 记录每颗珍珠是否被移除
ll mod=998244353;
void solve()
{
ll x,y,m,i,j,n;
cin>>n;
for(i=1;i<=n;i++)
cin>>a[i];
dp[0]=1;vis[0]=1;
for(i=1;i<=n;i++)//枚举物品
{
if(a[i])//只有大于0才能被选中
{
for(j=n;j>=a[i];j--)//枚举体积
{
vis[j]|=vis[j-a[i]];//更新珍珠状态
dp[j]=(dp[j]+dp[j-a[i]])%mod;//更新方案数量
}
}
}
ll ans=n;
while(!vis[ans]) ans--;//求最大的移除数量
cout<<ans<<" "<<dp[ans];
}
int main()
{
int t=1;
//cin>>t;
while(t--) solve();
return 0;
}