由于换季,商场推出优惠活动,以超低价格出售若干种商品。但是商场为避免过分亏本,规定某些商品不能同时购买,而且每种超低价商品只能买一件。身为顾客的你想获得最大的实惠,也就是争取节省最多的钱。经过仔细研究,我们发现商场出售的超低价商品中,不存在以下这种情况:n(n>=3)种商品C1,C2,C3,……,Cn,其中Ci和Ci+1是不能同时购买的(i=1,2,……,n-1),而且C1和Cn也不能同时购买。
请编程计算可以节省的最大金额数。
对于这种完全没有思路的题,还是先多看题目吧。忽然发现有一个有意思的条件: 不存在以下这种情况:n(n>=3)种商品C1,C2,C3,……,Cn,其中Ci和Ci+1是不能同时购买的(i=1,2,……,n-1),即为不存在连续>=3个的不能连续购买,且不会有环。
接着重新分析题目,一个点连了一个不能购买的点,这个不能购买的点又连了一个不能购买的点,再加上刚刚所分析的特殊条件,那么我们便可以发现,题目所给的限制条件是一个类似于树形结构了,且对于每一个树上的子节点与父节点,只能选一个。
这时就可以构造状态了,dp[i][1/0]表示第i个点选或不选,从下向上更新答案,也就没有了后效性。
接着可以考虑转移,从一棵树上如果这个点选了,那么只能从它子节点里没有选的转移过来,如果这个节点没有选,那么它的子节点选或不选都是可以的。
对于代码的实现部分,细节的一点是要注意题目是一个森林结构,所以在枚举每一个点时,也要确定建树的点没有重复。DFS的建树后,注意自底向上建树即可。特别的,如果这个点和任何一个点都没有边,那么它肯定是要选的。
下附AC代码。
#include<iostream> #include<stdio.h> #include<algorithm> #include<string.h> #include<vector> #define maxn 1005 using namespace std; int n,k; int ans=0; int val[maxn]; int vis[maxn]; int flag[maxn]; int dp[maxn][10]; vector<int> edge[maxn]; vector<int> nex[maxn]; void dfs(int now) { vis[now]=1; int len=edge[now].size(); for(int i=0;i<len;i++) if(vis[edge[now][i]]==0) { nex[now].push_back(edge[now][i]); dfs(edge[now][i]); } } void findans(int now) { int len=nex[now].size(); if(len==0) { dp[now][1]=val[now]; dp[now][0]=0; return; } for(int i=0;i<len;i++) findans(nex[now][i]); for(int i=0;i<len;i++) { int ne=nex[now][i]; dp[now][1]+=dp[ne][0]; dp[now][0]+=max(dp[ne][1],dp[ne][0]); } dp[now][1]+=val[now]; return; } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d",&val[i]); } for(int i=1;i<=k;i++) { int x,y; scanf("%d%d",&x,&y); edge[x].push_back(y); edge[y].push_back(x); } for(int i=1;i<=n;i++) { if(edge[i].size()==0) { vis[i]=1; ans+=val[i]; } else if(vis[i]==0) { dfs(i); findans(i); ans+=max(dp[i][0],dp[i][1]); } } printf("%d\n",ans); }