bzoj4247:挂饰
Description
JOI君有N个装在手机上的挂饰,编号为1...N。 JOI君可以将其中的一些装在手机上。
JOI君的挂饰有一些与众不同——其中的一些挂饰附有可以挂其他挂件的挂钩。每个挂件要么直接挂在手机上,要么挂在其他挂件的挂钩上。直接挂在手机上的挂件最多有1个。
此外,每个挂件有一个安装时会获得的喜悦值,用一个整数来表示。如果JOI君很讨厌某个挂饰,那么这个挂饰的喜悦值就是一个负数。
JOI君想要最大化所有挂饰的喜悦值之和。注意不必要将所有的挂钩都挂上挂饰,而且一个都不挂也是可以的。
Input
第一行一个整数N,代表挂饰的个数。
接下来N行,第i行(1<=i<=N)有两个空格分隔的整数Ai和Bi,表示挂饰i有Ai个挂钩,安装后会获得Bi的喜悦值。
Output
输出一行一个整数,表示手机上连接的挂饰总和的最大值
Sample Input
5
0 4
2 -2
1 -1
0 1
0 3
0 4
2 -2
1 -1
0 1
0 3
Sample Output
5
HINT
将挂饰2直接挂在手机上,然后将挂饰1和挂饰5分别挂在挂饰2的两个挂钩上,可以获得最大喜悦值4-2+3=5。
1<=N<=2000
0<=Ai<=N(1<=i<=N)
-10^6<=Bi<=10^6(1<=i<=N)
1A的一道题,好开心
题解:
看到这道题,经验主义感觉应该是dp,可是我一开始并不知道该怎么dp
想到在确定了最大情况下挂多少挂件后,挂件挂的顺序并没有影响,于是先将挂件根据挂钩从大到小排序
之后想状态应是什么
曾经想过把挂件间的连接关系当成一棵树,类似树形dp做,但是不好实现,子树中可能有重复的挂件
然后大概就是考虑每一个挂件是挂还是不挂
慢慢想到了可以用记忆化搜索,状态是从某一个挂件开始往后,在还有多少挂钩的可获得的最大喜悦值(似乎说的不太明白,看代码就一目了然了)
做的时候遇到了一个问题,就是还剩的挂钩数可能会很大。但很快发现剩的挂钩数若大于n是毫无意义的,于是所有大于n的都当成n就好了
总结一下思路:
感觉是dp -> 思考状态,针对每一个挂钩考虑 -> 记忆化搜索 -> 改进细节
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #include<string.h> 5 #define INF 100000000 6 using namespace std; 7 8 const int MAXN = 2005; 9 int n; 10 struct item{ 11 int a,b; 12 bool operator < (const item &x) const{ 13 return x.a>a; 14 } 15 }d[MAXN]; 16 bool cmp(item x,item y){ 17 return y<x; 18 } 19 20 int dp[MAXN][MAXN],vis[MAXN][MAXN]; 21 int dfs(int num,int free){ 22 if(free>n) free=n; 23 if(vis[num][free]) return dp[num][free]; 24 if(free==0) return 0; 25 if(num==n+1) return 0; 26 int ret=-INF; 27 ret=max(ret,dfs(num+1,free-1+d[num].a)+d[num].b); 28 ret=max(ret,dfs(num+1,free)); 29 vis[num][free]=1; 30 return dp[num][free]=ret; 31 } 32 33 int main() 34 { 35 int i,j; 36 scanf("%d",&n); 37 for(i=1;i<=n;i++) scanf("%d%d",&d[i].a,&d[i].b); 38 sort(d+1,d+1+n,cmp); 39 40 printf("%d\n",dfs(1,1)); 41 42 return 0; 43 }
既然选择了远方,便只顾风雨兼程