有n种硬币,现在我们需要凑出t价值的硬币,我们每种硬币都可以任取数量,不过要满足q个约束。
对于每个约束,(a,b)表示需要a种类的硬币使用的个数多余b种类的硬币。
思路:
①对于依赖(a,b),如果我们拿了一个b硬币,那么肯定同时也要拿至少一个a硬币,那么我们可以预处理一下,令一开始的时候,我们是满足所有约束条件的。
那么过程其实就相当于建个反图,然后将t-=a【i】*d,d表示从度为0的点拓扑到当前点链的长度。这样的话我们就能够使得初始的时候,我们是满足所有约束条件的。
②那么接下来,我们考虑拿一个b硬币的时候,同时也要再拿一个a硬币才能继续保证满足约束条件。所以我们再拓扑排序一下过程使得a【v】+=a【u】,然后构建出新的物品集合,然后再跑完全背包即可。
具体细节参考代码;
Ac代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
#define mod 1000000007
#define ll __int64
ll wupin[350];
vector<ll>mp[350];
ll a[350];
ll b[350];
ll dp[150000];
ll degree[150000];
ll vis[150000];
ll n,q,t;
void Top()
{
queue<ll>s;
memset(dp,0,sizeof(dp));
for(ll i=1;i<=n;i++)
{
if(degree[i]==0)
{
s.push(i);
}
}
ll cnt=0;
int cont=0;
while(!s.empty())
{
ll u=s.front();s.pop();
cont++;
if(vis[u]>0)t-=a[u]-b[u];
if(a[u]<=t)wupin[cnt++]=a[u];
for(ll i=0;i<mp[u].size();i++)
{
ll v=mp[u][i];
a[v]+=a[u];
degree[v]--;
if(degree[v]==0)s.push(v);
}
}
dp[0]=1;
for(ll i=0;i<cnt;i++)
{
for(ll j=wupin[i];j<=t;j++)
{
dp[j]+=dp[j-wupin[i]];
dp[j]%=mod;
}
}
if(t>=0&&cont==n)
printf("%I64d\n",dp[t]);
else printf("0\n");
}
int main()
{
while(~scanf("%I64d%I64d%I64d",&n,&q,&t))
{
memset(vis,0,sizeof(vis));
memset(degree,0,sizeof(degree));
for(ll i=1;i<=n;i++)mp[i].clear();
for(ll i=1;i<=n;i++)scanf("%I64d",&a[i]),b[i]=a[i];
for(ll i=0;i<q;i++)
{
ll x,y;scanf("%I64d%I64d",&x,&y);
vis[x]++,vis[y]++;
mp[x].push_back(y);
degree[y]++;
}
Top();
}
}