题意:
一种dp:
小冰的N个机器人兄弟排成一列,每个机器人有一个颜色。现在小冰想让同一颜色的机器人聚在一起,即任意两个同颜色的机器人之间没有其他颜色的的机器人。假设任意相邻的两个机器人可以交换位置,最少需要多少次交换?N<16,颜色种类不超过16种。
解法:一个明显的结论是:交换机器人时,相同颜色的机器人不会发生交换(保持他们之间的相对顺序)。即相当于给16种排序颜色。这总共有16!种结果,其dp方法雷同于旅行商问题的方法。
两个代码:
一种记忆化搜索,复杂度2^16*16*16:
/******************************************************
* @author:xiefubao
*******************************************************/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <string.h>
//freopen ("in.txt" , "r" , stdin);
using namespace std;
#define eps 1e-8
#define zero(_) (abs(_)<=eps)
const double pi=acos(-1.0);
typedef long long LL;
const int Max=100010;
const LL INF=0x3FFFFFFFFFFFFFFF;
int a[Max];
LL num[20][20];
int sum[20];
LL ans[1<<17];
int n;
int k;
LL dfs(int state)
{
if(ans[state]!=-1)
return ans[state];
ans[state]=INF;
for(int i=0; i<k; i++)
{
if(state&(1<<i))
{
LL now=0;
for(int j=0; j<k; j++)
{
if(i==j)
continue;
if(state&(1<<j))
now+=num[i][j];
}
ans[state]=min(ans[state],now+dfs(state^(1<<i)));
}
}
if(ans[state]==INF&&state-(state&(-state))==0)
while(1);
return ans[state];
}
int main()
{
int t;
cin>>t;
int Case=1;
while(t--)
{
memset(num,0,sizeof num);
memset(sum,0,sizeof sum);
memset(ans,-1,sizeof ans);
scanf("%d%d",&n,&k);
for(int i=0; i<n; i++)
{
scanf("%d",a+i);
a[i]--;
}
for(int i=n-1; i>=0; i--)
{
for(int j=0; j<k; j++)
{
if(j==a[i])continue;
num[a[i]][j]+=sum[j];
}
sum[a[i]]++;
}
ans[0]=0;
for(int i=0; i<k; i++)
ans[1<<i]=0;
printf("Case #%d: ",Case++);
cout<<dfs((1<<k)-1)<<endl;
}
return 0;
}
一种dp:
复杂度2^16*16
/******************************************************
* @author:xiefubao
*******************************************************/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#include <stack>
#include <string.h>
//freopen ("in.txt" , "r" , stdin);
using namespace std;
#define eps 1e-8
#define zero(_) (abs(_)<=eps)
const double pi=acos(-1.0);
typedef long long LL;
const int Max=100010;
const LL INF=0x3FFFFFFFFFFFFFFF;
int a[Max];
LL num[20][20];
int sum[20];
LL ans[1<<17];
LL tool[1<<16][16];
int re[1<<16];
int n;
int k;
int main()
{
int t;
cin>>t;
int Case=1;
for(int i=0; i<16; i++)
re[1<<i]=i;
while(t--)
{
memset(num,0,sizeof num);
memset(sum,0,sizeof sum);
scanf("%d%d",&n,&k);
for(int i=0; i<n; i++)
{
scanf("%d",a+i);
a[i]--;
}
for(int i=n-1; i>=0; i--)
{
for(int j=0; j<k; j++)
{
if(j==a[i])continue;
num[a[i]][j]+=sum[j];
}
sum[a[i]]++;
}
ans[0]=0;
int tot=1<<k;
for(int i=0; i<k; i++)
{
tool[0][i]=0;
for(int j=1; j<tot; j++)
{
int tmp=j&(-j);
tool[j][i]=tool[j-tmp][i]+num[i][re[tmp]];
}
}
for(int i=1; i<tot; i++)
ans[i]=INF;
ans[0]=0;
for(int i=0; i<tot; i++)
{
for(int j=0; j<k; j++)
{
if(!(i&(1<<j)))
continue;
int tmp=i&(-i);
ans[i]=min(ans[i],ans[i-(1<<j)]+tool[i-(1<<j)][j]);
}
}
printf("Case #%d: ",Case++);
cout<<ans[tot-1]<<endl;
}
return 0;
}