原题链接:https://www.acwing.com/problem/content/285/
题意简单来说就是:给你一个环,断掉一条边使其成为一个链,用这个链跑dp,求最大得分。
首先这不是一道板子题。。。跟板子区别如下:
1.多了一重循环,最开始要求断掉一条边,但是我们不知道要断掉哪一条边,因此需要循环一遍来比较。
2.计算方法有加法有乘法,而且数据有正数有负数。这两个条件任意一个都不足惧,但是如果凑在一起。。。就很微妙了。
何出此言?比如说,我们用 f [ i ] [ j ]来表示从i号点到j号点的最大得分。那么假如,最后一次的运算是乘法。你在最后一次计算的时候,用于相乘的两个数,肯定都是它们各自那个区间的最优解,也就是最大的。然而,假如说在这个过程中,你淘汰了一些负数的值,那就很危险了。比如,你最后选择了1000和100相乘,但是本来你可以让-10000和-3000相乘的,那不就直接gg了吗。。
负负得正,真是麻烦。。。。
那怎么解决呢?其实也简单。只要另开一个数组记录最小得分即可~~;
3.题目还要求: 有些边如果在首次移动中被删除,可以导致最高得分。在输出文件的第二行,要求列举出所有这样的边,而且按照升序输出,其间用一个空格分开。(这句话直接粘贴的),也就是说,我们需要用一个数组把每条边和断掉该条边所能获得的最大得分匹配起来,以便最后比较。
说的差不多了,上代码:
1 #include <cstdio> 2 #include <algorithm> 3 #include <cmath> 4 #include <iostream> 5 #include <cstring> 6 using namespace std; 7 const int maxn=50+5,Inf=1e9+10; 8 int n,data[maxn<<1];//用来存储各点权;因为是环,所以左移一位; 9 int f[maxn<<1][maxn<<1][3];// f[i][j][1]表示从i号点到j号点的最大得分,f[i][j][2]表示从***到***的最小得分; 10 int ff[maxn<<1];//ff储存每条断边的最大得分,便于最后查找 ; 11 char c[maxn<<1]; //储存符号; 12 int q,w,e,r;//方便比较的变量。。 13 int ans_point=(-1)*Inf;//最大得分,先初始化为最小; 14 void Read(){ 15 cin>>n;//因为有字符+空格的输入。。所以cin比较方便; 16 for(int i=1;i<=n;++i){ 17 cin>>c[i]>>data[i]; 18 c[i+n]=c[i]; 19 data[i+n]=data[i]; 20 } 21 } 22 void Solve(){ 23 for(int i=1;i<=n;++i){//枚举断边,断边编号+n就是形成的链; 24 for(int j=1;j<=(n<<1);++j){//初始化; 25 for(int k=1;k<=(n<<1);++k){ 26 f[j][k][1]=(-1)*Inf;//求最大值; 27 f[j][k][2]=Inf;//求最小值; 28 } 29 } 30 for(int j=1;j<=(n<<1);++j){//初始化区间dp; 31 f[j][j][1]=f[j][j][2]=data[j]; 32 if(c[j+1]=='t') f[j][j+1][1]=f[j][j+1][2]=data[j]+data[j+1]; 33 else f[j][j+1][1]=f[j][j+1][2]=data[j]*data[j+1]; 34 } 35 for(int l=2;l<=n;++l){//枚举区间长度; 36 for(int j=i;j+l<=i+n;++j){//终止条件!!!小心出错;j是起点,k是终点; 37 int k=j+l-1;//把这里的l写成i了,找了半天错。。。吐了吐了; 38 for(int x=j;x<k;++x){//不可以等于k,因为有x+1; 39 if(c[x+1]=='t'){//读过题的都知道,c[x+1]连接的是x号节点和(x+1)号节点; 40 f[j][k][1]=max(f[j][k][1],f[j][x][1]+f[x+1][k][1]); 41 f[j][k][2]=min(f[j][k][2],f[j][x][2]+f[x+1][k][2]); 42 }else{ 43 q=f[j][x][1]*f[x+1][k][1]; 44 w=f[j][x][2]*f[x+1][k][2]; 45 e=f[j][x][1]*f[x+1][k][2]; 46 r=f[j][x][2]*f[x+1][k][1]; 47 f[j][k][1]=max(f[j][k][1],max(max(q,w),max(e,r))); 48 f[j][k][2]=min(f[j][k][2],min(min(q,w),min(e,r))); 49 } 50 } 51 } 52 } 53 ff[i]=f[i][i+n-1][1];//注意是i+n-1不是i+n。。小学学的植树问题不要搞错; 54 } 55 for(int i=1;i<=n;++i){//因为可能有多个最大边,所以先把最大得分算出来,然后。。 56 ans_point=max(ans_point,ff[i]); 57 } 58 printf("%d ",ans_point);//注意换行; 59 for(int i=1;i<=n;++i){//然后输出所有跟最大得分相等的边就行了; 60 if(ans_point==ff[i]) printf("%d ",i);//注意空格; 61 } 62 } 63 int main(){//芜湖,可怜的主函数,啥也没干; 64 Read(); 65 Solve(); 66 return 0; 67 }