问题在于代码能力不够,码农题写不出来,细节难以处理
以及知识点的欠缺
T3
常规背包无法进行,但注意到数据范围较小,考虑 搜索
2 n 2^n 2n 暴力可以拿到 30 30 30 pts,而对于这种 n n n 在 25 ∼ 40 25\sim40 25∼40 之间的数据,需要考虑 折半搜索
折半搜索,简单说就是搜索时先搜前一半的状态,将这些状态存进数据结构里,再搜后一半,对于后一半的每个状态,在数据结构中查找与之匹配的最优值,更新答案
这样,时间复杂度由 2 n 2^n 2n 降至 2 n 2 × ( 数据结构维护成本 ) 2^{\frac{n}{2}} \times(数据结构维护成本) 22n×(数据结构维护成本)
本题来说,看到异或和,当然想到 t r i e trie trie 树
状态匹配时,问题就是:在 t r tr tr 中查找最大 v a l val val ,并且使得在每一位上,当前选择的异或值都不高于 m m m
常规的 t r i e trie trie 套路
#include<bits/stdc++.h>
using namespace std ;
typedef long long LL ;
const int N = 40 ;
int n , m ;
struct nn
{
int v , w ;
}p[N] ;
int t[1<<22][2] , tot ;
LL maxval[1<<22] ;
void Insert( int x , LL V )
{
int p = 0 ;
for(int i = 30 ; i >= 0 ; i -- ) {
int c = (x>>i)&1 ;
if( !t[p][c] ) t[p][c] = ++tot ;
p = t[p][c] ;
maxval[p] = max ( maxval[p] , V ) ;
}
}
LL quary( int x , LL V )
{
int p = 0 ;
LL res = 0xcfcfcfcfcfcfcfcf ;
for(int i = 30 ; i >= 0 ; i -- ) {
int dm = (m>>i)&1 , dx = (x>>i)&1 ;
if( dm ) {
if( dx ) {
res = max( res , maxval[t[p][1]] ) ;
p = t[p][0] ;
}
else {
res = max( res , maxval[t[p][0]] ) ;
p = t[p][1] ;
}
}
else {
if( dx ) {
p = t[p][1] ;
}
else {
p = t[p][0] ;
}
}
if( !p ) return res + V ;
}
return max( res , maxval[p] + V ) ;
}
LL ans ;
int main()
{
scanf("%d%d" , &n , &m ) ;
for(int i = 1 ; i <= n ; i ++ ) {
scanf("%d" , &p[i].v ) ;
}
for(int i = 1 ; i <= n ; i ++ ) {
scanf("%d" , &p[i].w ) ;
}
int len = n / 2 ;
memset( maxval , 0xcf , sizeof maxval ) ;
for(int mask = 0 ; mask < (1<<len) ; mask ++ ) {
int sumV = 0 ;
LL sumW = 0 ;
for(int i = 0 ; i < len ; i ++ ) {
if( mask&(1<<i) ) {
sumV ^= p[i+1].v ;
sumW += p[i+1].w ;
}
}
Insert( sumV , sumW ) ;
}
for(int mask = 0 ; mask < (1<<(n-len)) ; mask ++ ) {
int sumV = 0 ;
LL sumW = 0 ;
for(int i = 0 ; i < n-len ; i ++ ) {
if( mask&(1<<i) ) {
sumV ^= p[i+len+1].v ;
sumW += p[i+len+1].w ;
}
}
ans = max( ans , quary( sumV , sumW ) ) ;
}
printf("%lld" , ans ) ;
return 0 ;
}
本题的另一种解法是 随机化
注意到 n n n 非常小,状态数不多,直接随机很大概率能搞出答案
srand(time(0)) ;
scanf("%d%d" , &n , &m ) ;
for(int i = 1 ; i <= n ; i ++ ) {
scanf("%d" , &p[i].v ) ;
id[i] = i ;
}
for(int i = 1 ; i <= n ; i ++ ) {
scanf("%d" , &p[i].w ) ;
}
while( clock() < 0.9*CLOCKS_PER_SEC ) {// 运行时间 <= 0.9
random_shuffle( id+1 , id+n+1 ) ;// 打乱 id 数组
int sumV = 0 ;
LL sumW = 0 ;
for(int i = 1 ; i <= n ; i ++ ) {
sumV ^= p[id[i]].v ;
sumW += p[id[i]].w ;
if( sumV <= m ) ans = max( ans , sumW ) ;
}
}
cout << ans ;
return 0 ;
}
考场上真不会 且 状态数不多 的题要大胆搞随机!!
T4
观察数据范围,对于
T
i
=
1
T_i=1
Ti=1 的情况,类比分层图,将一个点拆成:位置点、扫描锁车点、车站1、车站2…
图建出来后直接跑最短路即可
#include<bits/stdc++.h>
using namespace std ;
typedef long long LL ;
const int N = 1e6 + 10 , M = 2e6 + 10 ;
int n , r , s , X , T[N] ;
int inoutsubway[N] , takeother[N] ;
struct nn
{
int lst , to , val ;
}E[2*M] ;
int head[N] , tot , pit ;
void add( int x , int y , int val )
{
E[++tot] = (nn){ head[x] , y , val } ;
head[x] = tot ;
}
map<int,int> mp[N] ; // i 号点,第 j 个铁路站的 节点标号/所属铁路
LL dis[N] ;
bool vis[N] ;
struct dj
{
int id ;
LL val ;
friend bool operator < ( dj x , dj y ) {
return x.val > y.val ;
}
};
priority_queue<dj> q ;
void dij()
{
memset( dis , 0x3f , sizeof dis ) ;
dis[1] = 0 ;
q.push( (dj){ 1 , 0 } ) ;
while( !q.empty() ) {
int now = q.top().id ;
q.pop() ;
if( vis[now] ) continue ;
vis[now] = 1 ;
for(int i = head[now] ; i ; i = E[i].lst ) {
int t = E[i].to ;
if( dis[t] > dis[now] + E[i].val ) {
dis[t] = dis[now] + E[i].val ;
if( !vis[t] ) q.push( (dj){ t , dis[t] } ) ;
}
}
}
}
int main()
{
freopen("problem.in","r",stdin) ;
freopen("problem.out","w",stdout) ;
scanf("%d%d%d%d" , &n , &r , &s , &X ) ;
for(int i = 1 ; i <= n ; i ++ ) {
scanf("%d" , &inoutsubway[i] ) ;
add( i , i+n , X ) ; // 自行车扫锁
add( i+n , i , 0 ) ;
}
for(int i = 1 ; i <= n ; i ++ ) {
scanf("%d" , &takeother[i] ) ;
}
int x , y , t ;
for(int i = 1 ; i <= r ; i ++ ) {
scanf("%d%d%d" , &x , &y , &t ) ;
add( x+n , y+n , t ) , add( y+n , x+n , t ) ;
}
int K , lst ;
pit = 2*n ;
for(int i = 1 ; i <= s ; i ++ ) {
scanf("%d" , &K ) ;
scanf("%d" , &lst ) ;
if( !mp[lst][i] ) mp[lst][i] = ++pit ;
int top = mp[lst][i] ;
add( lst , top , inoutsubway[lst] ) ;
add( top , lst , inoutsubway[lst] ) ;
for(int j = 1 ; j <= K ; j ++ ) {
scanf("%d%d" , &x , &y ) ;
if( !mp[y][i] ) mp[y][i] = ++pit ;
top = mp[y][i] ;
add( y , top , inoutsubway[y] ) ;
add( top , y , inoutsubway[y] ) ;
add( mp[lst][i] , top , x ) ;
add( top , mp[lst][i] , x ) ; // 铁路双向
lst = y ;
}
scanf("%d" , &T[i] ) ;
}
for(int i = 1 ; i <= n ; i ++ ) { // 处理换乘
for( map<int,int> ::iterator it = mp[i].begin() ; it != mp[i].end() ; it ++ ) {
map<int,int> :: iterator iq = it ;
if( ++iq == mp[i].end() ) break ;
for( ; iq != mp[i].end() ; iq ++ ) {
add( it->second , iq->second , takeother[i] ) ;
add( iq->second , it->second , takeother[i] ) ;
}
}
}
dij() ;
for(int i = 1 ; i <= n ; i ++ ) {
printf("%lld " , dis[i] ) ;
}
return 0 ;
}
T5
曼哈顿距离,基于一个式子:
∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ = m a x ( ∣ ( x 1 + y 1 ) − ( x 2 + y 2 ) ∣ , ∣ ( x 1 − y 1 ) − ( x 2 − y 2 ) ∣ ) \mid x_1-x_2\mid+\mid y_1-y_2\mid \ = max(\ \mid (x_1+y_1)-(x_2+y_2)\mid\ ,\mid (x_1-y_1)-(x_2-y_2)\mid \ ) ∣x1−x2∣+∣y1−y2∣ =max( ∣(x1+y1)−(x2+y2)∣ ,∣(x1−y1)−(x2−y2)∣ )
%%% tql tql !!!!!