CF283 C Coin Troubles
http://www.codeforces.com/problemset/problem/283/C
有n中不同的硬币,但是他们的面值可能相同(其实无影响),有q个限制,某一种的数量要严格大于另外某一种。现在要凑成t面值,问有多少中方法,结果mod 1e9+7
首先应该想到没有q个限制的时候,是背包问题,可惜我这个背包我本来不会,后来看也没看懂,不知道是背包九讲中的哪一个。后来自己想了一下,相出了一个办法,和别人的代码一比较,发现是一样的,其实我想的办法就是背包的思路。分别用每一种硬币去更新凑成新面值的方案数,详情见代码。
问题是怎么把q个限制搞掉。
比如面值为a:1,b:2,a要多余b,我们可以把b的值变成a+b=3,每次用一个b,就相当于a和b一起用,这样就能保证a的数量永远大于等于b,那么题目要求严格大于,我们只需要在t里面减去一个a就可以了。这是两个硬币的关系,可以推到n个连续的关系上。
需要注意的是这些关系可能存在环,这样必然不能。
我是用双向链表写的dfs,比如有a>b>c>d ,那么深搜这条链,变成a,a+b,a+b+c,a+b+c+d,然后t减去3*a+2*b+c,这里错了好久,在减的时候超过了int的下限,变成了正的,导致后面背包越界了,只要有一瞬间t<0了,答案就是0。我就是被这里坑的!!!
贴代码
#include "iostream"
#include "cstdio"
#include "cstring"
#include "cmath"
#include "stdlib.h"
#include "algorithm"
using namespace std;
int flag=1,n,q,t ;
int f[100010];
typedef struct node
{
int val,from,to,vis;
} NODE;
NODE point[310];
void dfs(int x)
{
point[x].vis=1;
if(point[x].to==0)
return;
point[point[x].to].val+=point[x].val;
dfs(point[x].to);
t-=point[x].val;
if(t<0)
flag=0;//就是这里坑了我一晚上,坑到晚上12点,终于在电脑没电前一小会AC了
}
int main()
{
freopen("in.txt","r",stdin);
int x,y;
scanf("%d %d %d",&n,&q,&t);
memset(f,0,sizeof(f));
f[0]=1;
for(int i=1; i<=n; i++)
{
scanf("%d",&point[i].val);
point[i].from=point[i].to=point[i].vis=0;
}
for(int i=0; i<q; i++)
{
scanf("%d %d",&x,&y);
point[x].to=y;
point[y].from=x;
}
for(int i=1; i<=n; i++)
if(point[i].from==0)
dfs(i);
if(flag==0)
{
printf("0\n");
return 0;
}
for(int i=1; i<=n; i++)
if(point[i].vis==0)
{
printf("0\n");
return 0;
}
for(int i=1; i<=n; i++)
for(int j=0; j+point[i].val<=t; j++)
{
int tar=j+point[i].val;
int ftar=f[tar];
f[tar]=(f[tar]+f[j])%1000000007;
}
printf("%d\n",f[t]);
return 0;
}
水平有限,众神轻喷。