[题意]
2n颗宝石,n颗为阴,n颗为阳,阴阳交错的组成一个圈(圆排列)
给定一系列阴阳宝石对,如果在排列中这两颗宝石相邻,阳宝石就会失去能量
求如何排列能让最少的阳宝石失去能量,输出最少的宝石数目
[分析]
n比较小(
n<=9
),自然想到状压
但如果交错的枚举阴阳宝石,状态不好表示,也不好转移
于是想到先固定阴宝石的排列,再状压dp求阳宝石的最优放法
朴素做法 O(n!*2^n) TLE
xjb优化:
枚举排列时,用random_shuffle随机枚举,并加入最优性剪枝,调整了几次枚举次数后,就玄学的过掉了(而且跑得出奇的快)…
[代码]
#include <bits/stdc++.h>
using namespace std ;
const int N = 9 ;
const int inf = 0x3f3f3f3f ;
typedef long long LL ;
int T , n , m ;
int g[N][N] ;
int b[N] ;
int f[1<<N] ;
int L[1<<N] ;
int ans ;
int dp()
{
memset(f,inf,sizeof(f)) ;
f[0] = 0 ;
for( int s = 0 ; s < 1<<n ; s++ )
{
if( f[s] >= ans ) continue ;
int k = L[s] , kk = (L[s]+1)%n ;
for( int i = 0 ; i < n ; i++ )
{
if( s&(1<<i) ) continue ;
int ss = s|(1<<i) ;
f[ss] = min(f[ss],f[s]+(g[i][b[k]]|g[i][b[kk]])) ;
}
}
return f[(1<<n)-1] ;
}
int main()
{
for( int i = 1 ; i < 1<<N ; i++ )
L[i] = L[i-(i&-i)] + 1 ;
while( ~scanf( "%d%d" , &n , &m ) )
{
memset(g,0,sizeof(g)) ;
while( m-- )
{
int u , v ;
scanf( "%d%d" , &u , &v ) ;
u-- ; v-- ;
g[u][v] = 1 ;
}
if( n == 0 || m == 0 )
{
puts("0") ;
continue ;
}
for( int i = 0 ; i < n ; i++ )
b[i] = i ;
ans = inf ;
for( int i = 0 ; i < 1<<(n+1) ; i++ )
{
random_shuffle(b,b+n) ;
ans = min(ans,dp()) ;
}
printf( "%d\n" , ans ) ;
}
return 0 ;
}
[更新]
经菊苣们指导,枚举阴后,放阳可以构造二分图,跑匈牙利,要快很多
具体做法是,确定阴的排列后,若某个阳宝石放在某个位子上不会失去能量,则把阳宝石与该位子连边
答案为 n-最大匹配
这样朴素枚举圆排列就能过了,加上玄学随机后更是跑到了15ms…不知道是不是数据太水…
[代码]
#include <bits/stdc++.h>
using namespace std ;
const int N = 9 ;
const int inf = 0x3f3f3f3f ;
typedef long long LL ;
int T , n , m ;
int g[N][N] , b[N] ;
int link[N*2+5] , vis[N*2+5] ;
int ans ;
vector<int> G[N*2] ;
bool dfs( int u )
{
for( int v : G[u] )
{
if( vis[v] ) continue ;
vis[v] = 1 ;
if( link[v] == -1 || dfs(link[v]) )
{
link[v] = u ;
return 1 ;
}
}
return 0 ;
}
int hungury()
{
for( int i = 0 ; i < n<<1 ; i++ ) G[i].clear() ;
for( int i = 0 ; i < n ; i++ )
for( int j = 0 ; j < n ; j++ )
if( !g[i][b[j]] && !g[i][b[(j+1)%n]] )
G[i].push_back(j+n) , G[j+n].push_back(i) ;
int cnt = 0 ;
memset(link,-1,sizeof(link)) ;
for( int i = 0 ; i < n ; i++ )
{
memset(vis,0,sizeof(vis)) ;
cnt += dfs(i) ;
}
return cnt ;
}
int main()
{
while( ~scanf( "%d%d" , &n , &m ) )
{
memset(g,0,sizeof(g)) ;
while( m-- )
{
int u , v ;
scanf( "%d%d" , &u , &v ) ;
u-- ; v-- ;
g[u][v] = 1 ;
}
if( n == 0 || m == 0 )
{
puts("0") ;
continue ;
}
for( int i = 0 ; i < n ; i++ )
b[i] = i ;
ans = inf ;
do
{
ans = min(ans,n-hungury()) ;
}while( next_permutation(b,b+n) && !b[0] ) ;
/* 玄学随机
for( int i = 0 ; i < 1<<(n+1) ; i++ )
{
random_shuffle(b,b+n) ;
ans = min(ans,n-hungury()) ;
}*/
printf( "%d\n" , ans ) ;
}
return 0 ;
}