EOJ:Pro-Test Voting
——PROBLEM:某人竞选,须在各个社区争取选票,投入一定资金能够提高支持率,问如何分配资金能够使支持的人最多,并输出方案(同等条件下使第一个社区的投入最多,如果相同则第二个社区最多,依次类推)
——背包问题,麻烦在于输出方案
——url:http://202.120.106.94/onlinejudge/problemshow.php?pro_id=542
————————————————————————————————————————————————————————————
别被公式吓到,其实是个很简单的背包问题
方程:dp[i][j]=max(dp[i-1][k]+tmp) tmp为投入J-K这么多钱给第I个社区所能得到的支持人数。
赛时一直没有AC,问题出在输出方案
原先的想法是:
dp[i][j]=max(dp[i-1][k]+tmp) J=0->M K=J->0
即同等条件下取前面用的钱多的方案。
但是这是有问题的
例如:
假设有三个社区,有两种方案,分别是每个社区投入23 45 20和21 50 17
在转移的时候,dp[3][88]会从dp[2][21+50]转过来而不是dp[2][23+45],而这显然不符合题目的要求。
正确的解法:
将所有的小区倒过来,即原来最后一个小区变成现在第一个小区,原来第一个小区变成现在最后一个小区。(以下均是以此为基础)
dp[i][j]=max(dp[i-1][k]+tmp)
同等条件下取J-K最大的,即当前这个小区投入资金最多的。
如此一来,当前面的小区(即原来后面的小区)的状态一定时,当前这个小区用的钱是最多的,依次类推,即最后一个小区(原来第一个小区)用的钱是最多的。
#include<stdio.h> #include<stdlib.h> #include<memory.h> #define maxm 105 int dp[maxm][maxm],f[maxm][maxm],ans[maxm]; int i,j,k,tmp,m,n,loc,cas; double delta[maxm],Ip[maxm],N[maxm]; int min(int a,int b) { return a<b?a:b; } int solve(int a,double b) { double tmp; int t; tmp=(b/(b+10.1)*delta[a]+Ip[a])/100*N[a]; t= (int)(tmp+0.5); return min(t,N[a]); } int main() { cas=0; while (1) { cas++; scanf("%d%d",&m,&n); if (m==0&&n==0) break; memset(dp,0,sizeof(dp)); memset(ans,0,sizeof(ans)); dp[n+1][0]=0; for (i=1;i<=n;i++) scanf("%lf%lf%lf",&N[i],&Ip[i],&delta[i]); for (i=n;i>=1;i--) for (j=m;j>=0;j--) for (k=0;k<=j;k++) { tmp=solve(i,j-k); if (dp[i+1][k]+tmp>dp[i][j]) { dp[i][j]=dp[i+1][k]+tmp; f[i][j]=k; } } tmp=m; loc=1; while (loc!=n+1) { ans[loc]=tmp-f[loc][tmp]; tmp=f[loc][tmp]; loc++; } printf("Case %d: %d\n",cas,dp[1][m]); for (i=0;i<n-1;i++) printf("%d:%d ",i,ans[i+1]); if (n>0) printf("%d:%d\n",n-1,ans[n]); } return 0; }