首页 > 代码库 > POJ3666

POJ3666

题目链接:http://poj.org/problem?id=3666

题目大意:

  有一个由 N 个数组成的数组num,要求通过加减其中元素的数字把它变成一个不减数列或者不增数列。让一个数字加 1 的花费为 1,求最小花费。

AC思路:

  DP题。拿着个假算法搞了半天然后明智地放弃了。思路来源于网上。

  我们先来讨论如何用最小花费让数列变成不减数列。

  dp[i][j] 表示对于前 i 个元素,最大数(也可以理解为是最后一个数)是 j 时的最小花费。则 dp[i][j] = abs(num[i]-j) + min(dp[i-1][k], 0<=k<j).

  Warning! 数据类型要用 long long 否则 WA!

  要离散化。

AC代码:

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cmath>
 4 #include <cstdio>
 5 #include <map>
 6 using namespace std;
 7 typedef long long ll;
 8 const int maxn=2000+5;
 9 const ll inf=0x7fffffff;
10 map<ll,int> lists;
11 ll A[maxn],As[maxn];
12 ll dp[maxn][maxn];
13 ll abss(ll a,ll b){
14     if(a>b) return a-b;
15     return b-a;
16 }
17 int main(){
18     int N;
19     scanf("%d",&N);
20     int num=0;
21     for(int i=0;i<N;i++){
22         scanf("%d",&A[i]);
23         if(lists[A[i]]==0){
24             lists[A[i]]=1;
25             As[num++]=A[i];
26         }
27     }
28     sort(As,As+num);
29     for(int i=0;i<num;i++)
30         lists[As[i]]=i;
31     for(int i=0;i<num;i++)
32         for(int j=0;j<N;j++)
33             dp[i][j]=inf;
34     ll ans=inf;
35     for(int i=0;i<N;i++){
36         ll mm;
37         if(i==0)    mm=0;
38         else    mm=dp[i-1][0];
39         for(int j=0;j<num;j++){
40             if(i!=0)    mm=min(mm,dp[i-1][j]);
41             dp[i][j]=abss(As[j],A[i])+mm;
42             if(i==N-1)  ans=min(ans,dp[i][j]);
43         }
44     }
45     printf("%lld\n",ans);
46     return 0;
47 }

 

POJ3666