【ACDream】1409 Musical Andrew Stankevich Contest 21 状压DP模拟

传送门:【ACDream】1409 Musical Andrew Stankevich Contest 21


题目分析:我已经不想吐嘈了。。。。。这道模拟整死我了。。。

各种数组,各种注释。。。如果没有注释我怕还写不出来。。

题目很简单,,就是调的很辛苦T U T

就是简单状压DP+恶心的模拟。


代码如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std ;
 
typedef long long LL ;
#define rep( i , a , b ) for ( int i = a ; i < b ; ++ i )
#define For( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define rev( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define travel( e , H , u ) for ( Edge* e = H[u] ; e ; e = e -> next )
#define clr( a , x ) memset ( a , x , sizeof a )
#define cpy( a , x ) memcpy ( a , x , sizeof a )

const int MAXN = 11 ;

struct Pre {
	int S , idx ;
} pre[1 << MAXN][MAXN] ;//前驱

int city[MAXN][3] ;//每个城市的每种人的人数
int G[MAXN][MAXN] ;//G[u][v] = 1表示u,v之间存在边
int vis[MAXN][MAXN][MAXN] ;//前i天第j个城市第k个乐队是否表演过。
int perform[MAXN][MAXN][MAXN] ;//第i天第j个城市第k个乐队是有表演。
int sum[MAXN][MAXN][MAXN] ;//前i天第j个城市周围的城市被第k个乐队表演过的数目。
int in[MAXN][MAXN] ;//第i天第j个城市有多少个乐队有表演
int all[MAXN][MAXN] ;//第i天第j个城市及周围有多少个乐队有表演
double dp[1 << MAXN][MAXN] ;
int n , m , K , tot ;

void clear () {
	tot = 1 << n ;
	clr ( G , 0 ) ;
	clr ( in , 0 ) ;
	clr ( all , 0 ) ;
	clr ( vis , 0 ) ;
	clr ( pre , -1 ) ;
	clr ( sum , 0 ) ;
	clr ( perform , 0 ) ;
	rep ( i , 0 , tot ) rep ( j , 0 , n ) dp[i][j] = -1 ;
}

int count ( int x ) {
	return x ? count ( x >> 1 ) + ( x & 1 ) : 0 ;
}

void print ( int S , int idx ) {
	if ( pre[S][idx].idx != -1 ) print ( pre[S][idx].S , pre[S][idx].idx ) ;
	printf ( "%d%c" , idx + 1 , S == tot - 1 ? '\n' : ' ' ) ;
}

void solve () {
	clear () ;
	rep ( i , 0 , n ) rep ( j , 0 , 3 ) scanf ( "%d" , &city[i][j] ) ;
	rep ( i , 0 , n ) G[i][i] = 1 ;
	rep ( i , 0 , m ) {
		int u , v ;
		scanf ( "%d%d" , &u , &v ) ;
		-- u ;
		-- v ;
		G[u][v] = G[v][u] = 1 ;
	}
	rep ( k , 0 , K ) rep ( i , 0 , n ) {
		int x ;
		scanf ( "%d" , &x ) ;
		-- x ;
		vis[i][x][k] = 1 ;
		perform[i][x][k] = 1 ;
		in[i][x] ++ ;
	}
	rep ( i , 1 , n ) rep ( j , 0 , n ) rep ( k , 0 , K ) vis[i][j][k] |= vis[i - 1][j][k] ;
	rep ( i , 0 , n ) rep ( j , 0 , n ) rep ( k , 0 , K ) { 
		rep ( l , 0 , n ) if ( G[j][l] && j != l ) {
			sum[i][j][k] += vis[i][l][k] ;
		}
	}
	rep ( i , 0 , n ) rep ( j , 0 , n ) rep ( l , 0 , n ) if ( G[j][l] ) all[i][j] += in[i][l] ;
	rep ( i , 0 , n ) {
		double tmp = 0 ;
		tmp += ( double ) city[i][0] / ( in[0][i] + 1 ) ;//第一类
		tmp += ( double ) city[i][1] / ( in[0][i] + 1 ) * 7 ;//第二类
		rep ( j , 0 , n ) if ( G[i][j] ) tmp += ( double ) city[j][2] / ( all[0][j] + 1 ) * 7 ;//第三类
		dp[1 << i][i] = tmp ;
	}
	rep ( S , 0 , tot ) {
		int now = count ( S ) - 1 ;//当前状态个数-1为当前星期
		int next = now + 1 ;//下一星期
		rep ( i , 0 , n ) if ( dp[S][i] >= 0 ) {//状态dp[S][i]存在
			rep ( j , 0 , n ) {
				if ( S & ( 1 << j ) ) continue ;//已经走过j了,跳过
				double tmp = 0 ;
				//第一种人
				int cnt = 0 , maxv = -1 ;
				rep ( k , 0 , K ) if ( perform[next][j][k] ) {
					if ( sum[now][j][k] > maxv ) {
						cnt = 1 ;
						maxv = sum[now][j][k] ;
					} else if ( sum[now][j][k] == maxv ) ++ cnt ;
				}
				int cnt2 = 0 ;
				rep ( l , 0 , n ) if ( S & ( 1 << l ) ) if ( G[j][l] ) ++ cnt2 ;
				if ( cnt2 > maxv ) tmp += city[j][0] ;
				else if ( cnt2 == maxv ) tmp += ( double ) city[j][0] / ( cnt + 1 ) ;
				
				//第二种人
				tmp += ( double ) city[j][1] / ( in[next][j] + 1 ) * 7 ;
				
				//第三种人
				rep ( l , 0 , n ) if ( G[j][l] ) tmp += ( double ) city[l][2] / ( all[next][l] + 1 ) * 7 ;
				
				//统计
				if ( tmp + dp[S][i] > dp[S | ( 1 << j )][j] ) {
					dp[S | ( 1 << j )][j] = tmp + dp[S][i] ;
					pre[S | ( 1 << j )][j].S = S ;
					pre[S | ( 1 << j )][j].idx = i ;
				}
			}
		}
	}
	double ans = 0 ;
	int idx = 0 ;
	rep ( i , 0 , n ) if ( ans < dp[tot - 1][i] ) {
		ans = dp[tot - 1][i] ;
		idx = i ;
	}
	printf ( "%.10f\n" , ans ) ;
	print ( tot - 1 , idx ) ;
}

int main () {
	while ( ~scanf ( "%d%d%d" , &n , &m , &K ) ) solve () ;
	return 0 ;
}


你可能感兴趣的:(ACdream)