【狀壓DP】BZOJ4479吃貨jyy
阿新 • • 發佈:2018-11-07
分析:
把問題轉化一下,所謂的迴路只需要滿足:每個點得度數均為偶數即可。
然後可以先把必走的邊忽略,讓這些邊聯通且滿足上述條件。
做
的DP,表示每一個點:不連通/度數為奇/度數為偶。(這裡要特別注意必走邊,儘管我們忽略了它們,但是它們是實際存在的,要參與連通性計算,只不過代價為0,因為它們的代價我們會最後加上)
然後再搞一個 的DP,表示連結某些點的最小代價。
然後對每個 的狀態,找到哪些點應該補度數。求出一個對應的 的狀態。兩個狀態之和加上最開始忽略的必走邊就是答案。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define SF scanf
#define PF printf
#define MAXS 1594423
#define MAXN 14
#define INF 0x3f3f3f3f
#define MAXB 8292
using namespace std;
int n,m,w1;
int W;
struct node{
int x;
node *nxt;
}edge[MAXN*10];
node *head[MAXN],*ncnt=edge;
int b[MAXN],p[MAXN];
int dp[MAXS],f[MAXB];
void add_edge(int x,int y){
ncnt++;
ncnt->x=y;
ncnt->nxt=head[x];
head[x]=ncnt;
}
int c(int s,int pos){
return (s/p[pos])%3;
}
int tr(int s,int pos){
if(c(s,pos)==1)
return s+p[pos];
else
return s-p[pos];
}
int dist[MAXN][MAXN];
void prepare(){
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
b[0]=1;
p[0]=1;
for(int i=1;i<=n;i++){
b[i]=b[i-1]*2;
p[i]=p[i-1]*3;
}
memset(f,0x3f,sizeof f);
f[0]=0;
for(int mask=0;mask<b[n];mask++){
if(mask==INF)
continue;
for(int i=0;i<n;i++){
if(mask&b[i])
continue;
for(int j=0;j<n;j++){
if((mask&b[j])||j==i)
continue;
f[mask|b[i]|b[j]]=min(f[mask|b[i]|b[j]],f[mask]+dist[i][j]);
}
}
}
}
void Update(int &x,int y){
x=min(x,y);
}
int used[MAXN];
queue<int> q;
void solve(){
memset(dp,INF,sizeof dp);
dp[2]=0;
q.push(2);
while(!q.empty()){
int mask=q.front();
q.pop();
int tmp=0;
for(int i=0;i<n;i++)
if(c(mask,i)!=0)
used[tmp++]=i;
for(int i=0;i<n;i++)
if(c(mask,i)==0){
int mask1=mask+2*p[i];
for(node *v=head[i];v!=NULL;v=v->nxt)
if(c(mask1,v->x)!=0){
if(dp[mask1]==INF)
q.push(mask1);
Update(dp[mask1],dp[mask]);
}
for(int j=0;j<tmp;j++){
if(dist[i][used[j]]==INF)
continue;
mask1=tr(mask,used[j])+p[i];
if(dp[mask1]==INF)
q.push(mask1);
Update(dp[mask1],dp[mask]+dist[i][used[j]]);
}
}
}
}
bool check(int mask){
for(int i=0;i<n;i++)
if(head[i]!=NULL&&c(mask,i)==0)
return 0;
return 1;
}
int d[MAXN];
int get_ans(){
int res=INF;
for(int mask=2;mask<p[n];mask++){
if(check(mask)==0)
continue;
int t=0;
for(int i=0;i<n;i++){
if((d[i]^((mask/p[i])%3==1))==1){
t|=b[i];
}
}
//PF("{%d %d %d %d %d %d %d}\n",mask,t,((mask/p[0])%3==1)^d[0],d[1],d[2],d[3],d[4]);
Update(res,dp[mask]+f[t]);
}
return res;
}
int main(){
SF("%d%d",&n,&m);
memset(dist,INF,sizeof dist);
int u,v;
for(int i=1;i<=m;i++){
SF("%d%d%d",&u,&v,&w1);
u--;
v--;
W+=w1;
Update(dist[u][v],w1);
dist[v][u]=dist[u][v];
add_edge(u,v);
add_edge(v,u);
d[u]^=1;
d[v]^=1;
}
SF("%d",&m);
for(int i=1;i<=m;i++){
SF("%d%d%d",&u,&v,&w1);
u--;
v--;
Update(dist[u][v],w1);
dist[v][u]=dist[u][v];
}
for(int i=0;i<n;i++)
dist[i][i]=0;
prepare();
solve();
PF("%d",get_ans()+W);
}