Description
Input
Output
Sample Input
4
t -7 t 4 x 2 x 5
Sample Output
33
1 2
题意:已知一个环形的圆 有n个点和n条边 现在让你任意去掉一条边 然后每次根据边的符号合并两个点 要求合并成1个点的最大值
思路:和石子合并很像 我们只需要把链扩大为原来的两倍在这个长度上dp就行了 值得注意的是 最大值可能由两个最小值相乘得到 所以我们既要维护最大值同时也要记录最小值
在这个思路上区间dp即可
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<string> #include<vector> #include<stack> #include<bitset> #include<cstdlib> #include<cmath> #include<set> #include<list> #include<deque> #include<map> #include<queue> #define ll long long int using namespace std; inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} inline ll lcm(ll a,ll b){return a/gcd(a,b)*b;} int moth[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; int dir[4][2]={1,0 ,0,1 ,-1,0 ,0,-1}; int dirs[8][2]={1,0 ,0,1 ,-1,0 ,0,-1, -1,-1 ,-1,1 ,1,-1 ,1,1}; const int inf=0x3f3f3f3f; const ll mod=1e9+7; int dp[107][107][2]; char op[107]; int a[107]; int arry[107]; int main(){ ios::sync_with_stdio(false); int n; while(cin>>n){ for(int i=1;i<=n;i++){ cin>>op[i]>>a[i]; a[i+n]=a[i]; op[i+n]=op[i]; //两倍长链 } for(int i=1;i<=2*n;i++) //初始化边界 for(int j=1;j<=2*n;j++) dp[i][j][1]=inf,dp[i][j][0]=-inf; for(int i=1;i<=2*n;i++){ dp[i][i][0]=dp[i][i][1]=a[i]; //处理边界 } for(int len=2;len<=n;len++) //长度 for(int i=1;i+len<=2*n;i++){ //起点 int j=i+len-1; for(int k=i;k<j;k++){ //分割点 if(op[k+1]==‘t‘){ dp[i][j][0]=max(dp[i][j][0],dp[i][k][0]+dp[k+1][j][0]); dp[i][j][1]=min(dp[i][j][1],dp[i][k][1]+dp[k+1][j][1]); }else{ dp[i][j][0]=max(dp[i][j][0], max(dp[i][k][1]*dp[k+1][j][1],dp[i][k][0]*dp[k+1][j][0])); dp[i][j][1]=min(dp[i][j][1], min(dp[i][k][1]*dp[k+1][j][0], min(dp[i][k][1]*dp[k+1][j][1],dp[i][k][0]*dp[k+1][j][1]))); } } } int ans=-inf; int sz=0; for(int i=1;i<=n;i++){ if(ans<dp[i][i+n-1][0]){ ans=dp[i][i+n-1][0]; sz=1; arry[sz]=i; }else if(ans==dp[i][i+n-1][0]){ arry[++sz]=i; } } cout<<ans<<endl; for(int i=1;i<=sz;i++){ if(i==1) cout<<arry[i]; else cout<<" "<<arry[i]; } cout<<endl; } return 0; }
原文:https://www.cnblogs.com/wmj6/p/10800468.html