http://acm.hdu.edu.cn/showproblem.php?pid=5009
题意:一个数列,每个点代表一种颜色,每次选一个区间覆盖,覆盖的代价是区间内颜色种类数的平方,直到覆盖整个数列,求最小花费
思路:首先合并颜色相同的点,接着离散化颜色,做dp,dp[i]表示取到位置i的最小花费,注意到答案最大值应该是合并后的数列长度,这是一个剪枝,为了避免每次循环memset vis数组,要把每次访问的颜色值记录在一个vector中,然后只清vector内的颜色清空vector 即可
这道题总的来说出的感觉比较怪,时间卡的很死,复杂度也怪怪的(具体复杂度不会算,但觉得如此dp应该tle才对),还要用一些非常奇怪的小技巧(加vector数组)。题不难,但是网赛时能当场AC的人真的是胆大又自信。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std ; const int INF=0xfffffff ; int n,dp[50005] ; struct node { int num ; int id,rank ; }kk[50005] ; int a[50005] ; int vis[50005] ; int cmp1(node aa,node bb) { return aa.num<bb.num ; } int cmp2(node aa,node bb) { return aa.id<bb.id ; } int main() { while(~scanf("%d",&n)) { for(int i=1 ;i<=n ;i++) { scanf("%d",&a[i]) ; } int m=n ; for(int i=2 ;i<=n ;i++) { if(a[i]==a[i-1]) { m-- ; } } int cnt=2 ; kk[1].id=1 ;kk[1].num=a[1] ; for(int i=2 ;i<=n ;i++) { if(a[i]!=a[i-1]) { kk[cnt].id=cnt ; kk[cnt].num=a[i] ; cnt++ ; } } /* for(int i=1 ;i<=m ;i++) { printf("%d %d ",kk[i].num,kk[i].id) ; } printf("\n") ; */ sort(kk+1,kk+1+m,cmp1) ; kk[1].rank=1 ; cnt=2 ; for(int i=2 ;i<=m ;i++) { if(kk[i].num!=kk[i-1].num) { kk[i].rank=cnt++ ; } else kk[i].rank=kk[i-1].rank ; } sort(kk+1,kk+1+m,cmp2) ; /* for(int i=1 ;i<=m ;i++) { printf("%d ",kk[i].rank) ; } printf("\n") ; */ for(int i=0 ;i<50005 ;i++) dp[i]=INF ; dp[0]=0 ; dp[m]=m ; vector <int> v ; for(int i=0 ;i<m ;i++) { cnt=0 ; for(int j=i+1 ;j<=m ;j++) { if(!vis[kk[j].rank]) { v.push_back(kk[j].rank) ; vis[kk[j].rank]=1 ; cnt++ ; } if(dp[i]+cnt*cnt>=dp[m])break ; dp[j]=min(dp[j],dp[i]+cnt*cnt) ; } //memset(vis,0,sizeof(vis)) ; for(int j=0 ;j<v.size() ;j++) vis[v[j]]=0 ; v.clear() ; } printf("%d\n",dp[m]) ; } return 0 ; }