sgu 482 DP 482页

n块垂直放的宽度为1的木板 ,取走一些木板,剩下的按原来次序合在一起。满足 剩下的新图形周长>=原图形周长 ,求取走的木板最大的面积。



DP[i][j]  , 剩下的新图形最后一块为第i块,面积为j , 周长为dp[i][j]  。

当j 一定, dp[i][j] 取大为最优。

const  int  maxn = 58 ;
int    a[maxn]  ;
int    dp[maxn][100*maxn] ;
int    father[maxn][100*maxn]  ;
bool   vis[maxn]  ;

int  main(){
     int n  , i , j  , k ,  len , area  ,  t ;
     while(cin>>n){
          for(i = 1 ; i <= n ; i++) scanf("%d" , &a[i]) ;
          a[0] = 0 ;
          area = 0 ;
          len = 2*n ;
          for(i = 1 ; i <= n ; i++){
               area += a[i] ;
               len += abs(a[i] - a[i-1]) ;
          }
          len += a[n] ;
          memset(father , 0 , sizeof(father)) ;
          memset(dp , -1 , sizeof(dp))  ;
          dp[0][0] = 0 ;
          for(i = 1 ; i <= n ; i++){
               for(j = a[i] ; j <= area ; j++){
                    for(k = 0 ; k < i ; k++){
                         if(dp[k][j-a[i]] == -1) continue ;
                         int l = dp[k][j-a[i]] + 2 + 2 * a[i] - 2*min(a[i] , a[k]) ;
                         if(dp[i][j]==-1 || dp[i][j] < l){
                               dp[i][j] = l ;
                               father[i][j] = k ;
                         }
                    }
               }
          }
          int li , lj ;
          for(i = area ; i >= 0 ; i--){
                for(j = 1 ; j <= n ; j++){
                      if(dp[j][i] == -1) continue ;
                      if(dp[j][i]*2 >= len){
                          li = j ;
                          lj = i ;
                      }
                }
          }
          memset(vis , 0 , sizeof(vis)) ;
          printf("%d\n" , area - lj) ;
          if(area - lj != 0){
                while(li > 0 && lj > 0){
                      vis[li] = 1 ;
                      if(father[li][lj] == 0)  break  ;
                      t = father[li][lj] ;
                      lj -= a[li] ;
                      li = t ;
                }
                vector<int> lis ; lis.clear() ;
                for(i = 1 ; i <= n ; i++){
                      if(vis[i] == 0)  lis.push_back(i) ;
                }
                printf("%d\n" , lis.size()) ;
                for(i = 0 ; i < lis.size() -1 ; i++) printf("%d " , lis[i]) ;
                printf("%d\n" , lis[lis.size()-1]) ;
         }
         else  printf("0\n") ;
     }
     return 0 ;
}















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值