对16个颜色状压,dp[i]代表用了二进制i个颜色,满足题目要求的最小花费。。。那么每次我们就挑一种颜色放在这些颜色的最前面,然后就可以转移了。。。。可以先预处理出j放在i前面的花费,就是每个j前面有几个i的总和。。。。
#include <iostream> #include <queue> #include <stack> #include <map> #include <set> #include <bitset> #include <cstdio> #include <algorithm> #include <cstring> #include <climits> #include <cstdlib> #include <math.h> #include <time.h> #define maxn 100005 #define maxm 200005 #define eps 1e-7 #define mod 1000000007 //#define INF 0x3f3f3f3f #define PI (acos(-1.0)) #define lowbit(x) (x&(-x)) #define mp make_pair #define ls o<<1 #define rs o<<1 | 1 #define lson o<<1, L, mid #define rson o<<1 | 1, mid+1, R #define pii pair<int, int> #pragma comment(linker, "/STACK:16777216") typedef long long LL; typedef unsigned long long ULL; //typedef int LL; using namespace std; LL qpow(LL a, LL b){LL res=1,base=a;while(b){if(b%2)res=res*base;base=base*base;b/=2;}return res;} LL powmod(LL a, LL b){LL res=1,base=a;while(b){if(b%2)res=res*base%mod;base=base*base%mod;b/=2;}return res;} //head const LL INF = 0x3f3f3f3f3f3f3f3fLL; LL dp[1 << 17]; LL cost[20][20]; vector<int> g[20]; int n, kk; void work(int _) { int x; scanf("%d%d", &n, &kk); for(int i = 0; i < kk; i++) g[i].clear(); for(int i = 1; i <= n; i++) { scanf("%d", &x); g[--x].push_back(i); } for(int i = 0; i < kk; i++) for(int j = 0; j < kk; j++) { if(i == j) continue; LL sum = 0, cnt = 0; for(int k = 0; k < g[j].size(); k++) { while(g[i].size() > cnt && g[i][cnt] < g[j][k]) cnt++; sum += cnt; } cost[j][i] = sum; } memset(dp, 0x3f, sizeof dp); dp[0] = 0; for(int i = 0; i < (1 << kk); i++) { if(dp[i] == INF) continue; for(int j = 0; j < kk; j++) { if((i >> j) & 1) continue; LL sum = 0; for(int k = 0; k < kk; k++) { if(((i >> k) & 1) == 0) continue; sum += cost[j][k]; } dp[i | (1 << j)] = min(dp[i | (1 << j)], dp[i] + sum); } } printf("Case #%d: %lld\n", _, dp[(1 << kk) - 1]); } int main() { int _; scanf("%d", &_); for(int i = 1; i <= _; i++) work(i); return 0; }