标签:
http://poj.org/problem?id=1179
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 5078 | Accepted: 2139 |
Description
Input
Output
Sample Input
4 t -7 t 4 x 2 x 5
Sample Output
33 1 2
Source
多边形游戏,有N个顶点的多边形,3 <= N <= 50 ,多边形有N条边,每个顶点中有一个数字(可正可负),每条边上或者是“+”号,或者是“*”号。边从1到N编号,首先选择一条边移去,然后进行如下操作:
1 选择一条边E和边E连接着的两个顶点V1,V2。
2 用一个新的顶点代替边E和V1、V2,新顶点的值为V1、V2中的值进行边上代表的操作得来(相加或相乘)
当最后只剩一个顶点,没有边时,游戏结束。现在的任务是编程求出最后的顶点能获得的最大值,以及输出取该最大值时,第一步需移去的边,如果有多条符合条件的边,按编号从小到大输出。
其实结题思路还是比较好想到的,枚举(枚举去掉的符号)+DP(记忆化搜索)就可以做到。但这里有一个BUG,就是负负得正,所以不能单一的枚举最大值,而要同时DP最小值。
计算最大值:
加法 max(i,j) = max(i,k)+max(k,j);
乘法 max(i,j) = MAX(max(i,k)*max(k,j),max(i,k)*min(k,j),max(k,j)*min(i,k),min(i,k)*min(k,j));(i=<k<=j)
计算最小值:
加法 min(i,j) = min(i,k)+min(k,j);
乘法 min(i,j) = MIN(max(i,k)*max(k,j),min(i,k)*min(k,j),max(k,j)*min(i,k),min(i,k)*min(k,j));(i=<k<=j)
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <stack> 6 #include <set> 7 #include <queue> 8 #define MAX(a,b) (a) > (b)? (a):(b) 9 #define MIN(a,b) (a) < (b)? (a):(b) 10 #define mem(a) memset(a,0,sizeof(a)) 11 #define INF 1000000007 12 #define MAXN 20005 13 using namespace std; 14 15 bool op[105]; 16 int num[105],dp_max[10005], dp_min[10005], n; 17 bool vis_max[10005],vis_min[10005]; 18 int DP_MIN(int i,int j); 19 int DP_MAX(int i,int j); 20 21 22 int DP_MAX(int i,int j)//DP求区间最大值 23 { 24 int u = i*100+j; 25 if(vis_max[u])return dp_max[u]; 26 vis_max[u]=1; 27 if(j-i <= 1) 28 { 29 if(j==i)return dp_max[u]=num[i-1]; 30 if(!op[i])return dp_max[u]=num[i-1]+num[i]; 31 else return dp_max[u]=num[i-1]*num[i]; 32 } 33 dp_max[u] = -INF; 34 for(int k=i;k<j;k++) 35 { 36 int l=DP_MIN(i,k); 37 int r=DP_MIN(k+1,j); 38 int ll=DP_MAX(i,k); 39 int rr=DP_MAX(k+1,j); 40 if(!op[k])dp_max[u] = MAX(dp_max[u], ll+rr); 41 else dp_max[u] = MAX(dp_max[u], MAX(ll*rr,MAX(l*r,MAX(l*rr,r*ll)))); 42 } 43 return dp_max[u]; 44 } 45 46 int DP_MIN(int i,int j)//DP求区间最小值 47 { 48 int u = i*100+j; 49 if(vis_min[u])return dp_min[u]; 50 vis_min[u]=1; 51 if(j-i <= 1) 52 { 53 if(j==i)return dp_min[u]=num[i-1]; 54 if(!op[i])return dp_min[u]=num[i-1]+num[i]; 55 else return dp_min[u]=num[i-1]*num[i]; 56 } 57 dp_min[u] = INF; 58 for(int k=i;k<j;k++) 59 { 60 int l=DP_MIN(i,k); 61 int r=DP_MIN(k+1,j); 62 int ll=DP_MAX(i,k); 63 int rr=DP_MAX(k+1,j); 64 if(!op[k])dp_min[u] = MIN(dp_min[u], l+r); 65 else dp_min[u] = MIN(dp_min[u], MIN(ll*rr,MIN(l*r,MIN(l*rr,r*ll)))); 66 } 67 return dp_min[u]; 68 } 69 70 int main() 71 { 72 while(~scanf("%d%*c",&n)) 73 { 74 mem(op);mem(dp_max); 75 mem(num);mem(vis_min); 76 mem(vis_max); 77 int max=-INF,i; 78 char ch; 79 for(i=0;i<n;i++) 80 { 81 scanf("%c %d%*c",&ch,&num[i]); 82 op[i]=op[i+n]=(ch==‘x‘); 83 num[i+n]=num[i]; 84 } 85 for(i=0;i<n;i++) 86 { 87 max=MAX(max,DP_MAX(i+1,i+n)); 88 } 89 printf("%d\n",max); 90 int ok=1; 91 for(i=0;i<n;i++) 92 { 93 if(DP_MAX(i+1,i+n) == max) 94 { 95 if(ok){printf("%d",i+1);ok=0;} 96 else printf(" %d",i+1); 97 } 98 } 99 printf("\n"); 100 } 101 return 0; 102 }
1 #include<iostream> 2 using namespace std; 3 int v[51]; //存放点中的数据,下标从1开始 4 char op[51]; //存放边,下标从1开始 5 int max_score[51][51]; //max_score[i][j]表示从边i到边j所能取到的最大值 6 int min_score[51][51]; //min_score[i][j]表示从边i到边j所能取到的最小值 7 int n; //边、点的数量 8 9 //计算最大最小值 10 void calculate(int max_x, int min_x, char op, int max_y, int min_y, int &max, int &min) 11 { 12 if(op==‘t‘) 13 { 14 max = max_x + max_y; 15 min = min_x + min_y; 16 } 17 else //乘法 18 { 19 int temp; 20 max = min = max_x * max_y; 21 22 temp = max_x * min_y; 23 if(temp > max) max=temp; 24 if(temp < min) min=temp; 25 26 temp = min_x * max_y; 27 if(temp > max) max=temp; 28 if(temp < min) min=temp; 29 30 temp = min_x * min_y; 31 if(temp > max) max=temp; 32 if(temp < min) min=temp; 33 } 34 } 35 36 int getNum(int i) //处理可能的越界问题 37 { 38 if(i>n) i-=n; 39 else if(i<1) i += n; 40 return i; 41 } 42 43 //使用动态规划找最大值并返回 44 int dp() 45 { 46 int i,j,k,len, max_temp, min_temp, max_value, min_value, temp,ans; 47 //初始化数组的边界值 48 for(i=1; i<=n; i++) 49 { 50 j=i-1; //j代表i左边的边 51 if(j==0) j=n; 52 53 if(op[i]==‘t‘) 54 max_score[i][i] = min_score[i][i] = v[j] + v[i]; 55 else 56 max_score[i][i] = min_score[i][i] = v[j] * v[i]; 57 58 max_score[i][j] = min_score[i][j] = v[j]; 59 } 60 61 ans=1<<31; 62 for(len=2; len<n; len++) //len代表从边i到边j包含多少条边 63 for(i=1; i<=n; i++) //i代表开始边 64 { 65 j=i+len-1; //j代表结束边 66 if(j>n) j-=n; 67 //计算max[i][j] min[i][j]的值, max[i][j]=max{d[i][k-1] op[k] d[k+1][j]} i<=k<=j 68 k=i; 69 calculate(max_score[i][getNum(k-1)], min_score[i][getNum(k-1)], op[k], max_score[getNum(k+1)][j], min_score[getNum(k+1)][j], max_temp, min_temp); 70 max_value=max_temp; 71 min_value=min_temp; 72 while(true) 73 { 74 k++; 75 if(k>n) k-=n; 76 calculate(max_score[i][getNum(k-1)], min_score[i][getNum(k-1)], op[k], max_score[getNum(k+1)][j], min_score[getNum(k+1)][j], max_temp, min_temp); 77 if(max_temp > max_value) max_value=max_temp; 78 if(min_temp < min_value) min_value=min_temp; 79 if(k==j) break; 80 } 81 max_score[i][j]=max_value; 82 min_score[i][j]=min_value; 83 if(len==n-1 && max_value>ans) ans=max_value; 84 } 85 86 return ans; 87 } 88 89 int main() 90 { 91 int i,j,ans; 92 bool first=true;; 93 94 //读入数据 95 cin>>n; 96 for(i=1; i<=n; i++) 97 { 98 cin>>op[i]>>v[i]; 99 } 100 101 ans=dp(); 102 cout<<ans<<endl; //输出最大值 103 //输出取最大值时,第一次移去的边 104 for(i=2; i<=n; i++) 105 { 106 j=i+n-2; 107 if(j>n) j -= n; 108 109 if(max_score[i][j] == ans) 110 { 111 if(!first) cout<<" "; 112 cout<<i-1; 113 first = false; 114 } 115 } 116 if(max_score[1][n-1]==ans) 117 { 118 if(!first) cout<<" "; 119 cout<<n; 120 } 121 cout<<endl; 122 123 return 0; 124 }
标签:
原文地址:http://www.cnblogs.com/jeff-wgc/p/4455373.html