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;
}
浙公网安备 33010602011771号