zoukankan      html  css  js  c++  java
  • bzoj 3197 [Sdoi2013]assassin(Hash+DP+KM)

    Description

    Input

    Output

    Sample Input

    4
    1 2
    2 3
    3 4
    0 0 1 1
    1 0 0 0

    Sample Output

    1

    HINT

     

    【思路】

           Hash,DP,KM

           题目就是要找一个同构的树,使能够以最少的修改转换成目标状态。

           树的形态可以有多种但是他的中心只有一个。先找出中心,如果在边上则新建一个节点。以中心为根建树。同构的节点在树上是对称的。求出Hash。Hash函数如下:

                  H[u]=((((A*H[son1])*p+H[son2])*p+H[son3])*p)

       通过判断hash值和节点深度dep就可知道是否同构。

           在树上进行DP,设F[i][j]表示将j子树作为i子树同构时对应的最小花费。转移就是将i和j两个节点的儿子以最小权和进行完美匹配,匹配代价为F[soni][sonj],所以我们要按照dep序和hash将节点排一下序,这样相同的hash形成了一个区间,对于一个区间内的所有点对可以求出F,同时在求当前F的时候子结点的信息已经得到。

       因为形态唯一,所以最终答案为F[root][root]。

    【代码】

      1 #include<cmath>
      2 #include<queue>
      3 #include<vector>
      4 #include<cstdio>
      5 #include<cstring>
      6 #include<iostream>
      7 #include<algorithm>
      8 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
      9 using namespace std;
     10 
     11 typedef long long LL;
     12 const int N = 805; 
     13 const int INF = 1e9;
     14 const int A = 14221;
     15 const int B = 20707;
     16 const int P = 4481;
     17 const int MOD = 1060469; 
     18 
     19 struct Edge { int v,flag;
     20 };
     21 void read(int& x) {
     22     char c=getchar(); int f=1; x=0;
     23     while(!isdigit(c)) {if(c=='-')f=-1; c=getchar();}
     24     while(isdigit(c)) x=x*10+c-'0',c=getchar();
     25     x*=f;
     26 }
     27 
     28 vector<Edge> g[N];
     29 vector<int> tmp;
     30 queue<int> q;
     31 
     32 void adde(int u,int v) {
     33     g[u].push_back((Edge){v,0});
     34     g[v].push_back((Edge){u,0});
     35 }
     36 
     37 int n,root;
     38 int dis[N],dep[N],fa[N],vis[N];
     39 int firstST[N],finalST[N];
     40 LL H[N];
     41 
     42 struct KM {
     43     int g[N][N];
     44     int lx[N],rx[N];
     45     int l[N],r[N],res[N];
     46     int slack[N];
     47     
     48     void clear(int n) {
     49         FOR(i,1,n) FOR(j,1,n) g[i][j]=-1;
     50         FOR(i,1,n) res[i]=0;
     51     }
     52     
     53     bool find(int x,int n) {
     54         lx[x]=1;
     55         FOR(i,1,n)
     56             if(!rx[i] && g[x][i]!=-1) {
     57                 int tmp=g[x][i]-l[x]-r[i];
     58                 if(!tmp) {                                //相等子图 
     59                     rx[i]=1;
     60                     if(!res[i] || find(res[i],n)) {
     61                         res[i]=x;
     62                         return 1;
     63                     }
     64                 } else {
     65                     slack[i]=min(slack[i],tmp);            //更新 i 的 slack 
     66                 }
     67             }
     68         return 0;
     69     }
     70     int solve(int n) {
     71         if(!n) return 0;
     72         FOR(i,1,n) r[i]=0;
     73         FOR(i,1,n) {                                    //初始化顶标 
     74             l[i]=INF;
     75             FOR(j,1,n) if(g[i][j] != -1) {
     76                 l[i]=min(l[i],g[i][j]);
     77             }
     78         }
     79         FOR(i,1,n) {                                    //依次匹配 i 
     80             FOR(j,1,n) slack[j]=INF;
     81             int cnt=0;
     82             for(;;) {
     83                 cnt++;
     84                 FOR(j,1,n) lx[j]=rx[j]=0;
     85                 if(find(i,n)) break;
     86                 int mini=INF;                            //修改顶标使容纳更多的边 
     87                 FOR(i,1,n) if(!rx[i])                    //最小 slack S->T' 
     88                     mini=min(mini,slack[i]);
     89                 FOR(i,1,n) {
     90                     if(lx[i]) l[i]+=mini;                // S 和 T 集合的修改 保证原来的边依旧存在 
     91                     if(rx[i]) r[i]-=mini;
     92                     else slack[i]-=mini;                //改变 slack 
     93                 }
     94             }
     95         }
     96         int ans=0;
     97         FOR(i,1,n) ans+=l[i]+r[i];                        //相等子图的完美匹配 左右顶标之和 
     98         return ans;
     99     }
    100 } km;
    101 
    102 void calcHash(int u) {
    103     tmp.clear();
    104     FOR(i,0,(int)g[u].size()-1) {
    105         int v=g[u][i].v;
    106         if(v!=fa[u]&&!g[u][i].flag)
    107             tmp.push_back(H[v]);
    108     }
    109     sort(tmp.begin(),tmp.end());
    110     H[u]=A;
    111     FOR(i,0,(int)tmp.size()-1)
    112         H[u]=(H[u]*P%MOD ^ tmp[i])%MOD;
    113     H[u]=(H[u]*B) %MOD;
    114 }
    115 
    116 int bfs(int s) {
    117     memset(vis,0,sizeof(vis));
    118     q.push(s); vis[s]=1;
    119     int maxu=s; dis[s]=0;
    120     while(!q.empty()) {
    121         int u=q.front(); q.pop();
    122         FOR(i,0,(int)g[u].size()-1) {
    123             int v=g[u][i].v;
    124             if(!vis[v]) {
    125                 vis[v]=1; fa[v]=u; dis[v]=dis[u]+2;
    126                 if(dis[v]>dis[maxu]) maxu=v;
    127                 q.push(v);
    128             }
    129         }
    130     }
    131     return maxu;
    132 }
    133 void build(int u) {
    134     FOR(i,0,(int)g[u].size()-1) {
    135         int v=g[u][i].v;
    136         if(v!=fa[u] && !g[u][i].flag) {
    137             fa[v]=u;
    138             dep[v]=dep[u]+1;
    139             build(v);
    140         }
    141     }
    142     calcHash(u);
    143 }
    144 int getroot() {
    145     int x=bfs(1);
    146     int y=bfs(x);
    147     int mid=dis[y]/2,i;
    148     for(i=y;i!=x;i=fa[i]) {
    149         if(mid>=2) mid-=2;
    150         else break;
    151     }
    152     if(!mid) { fa[i]=-1; build(i); return i; }
    153     else {
    154         ++n;
    155         fa[n]=-1;
    156         adde(n,i); adde(fa[i],n);
    157         FOR(j,0,(int)g[i].size()-1)
    158             if(g[i][j].v==fa[i]) g[i][j].flag=1;
    159         FOR(j,0,(int)g[fa[i]].size()-1)
    160             if(g[fa[i]][j].v==i) g[fa[i]][j].flag=1;
    161         build(n);
    162         return n;
    163     }
    164 }
    165 
    166 bool cmp(const int& x,const int& y) {
    167     return dep[x]>dep[y] || (dep[x]==dep[y]&&H[x]<H[y]);
    168 }
    169 
    170 int F[N][N],pos[N];
    171 
    172 void DP() {
    173     memset(F,-1,sizeof(F));
    174     FOR(i,1,n) pos[i]=i;
    175     sort(pos+1,pos+n+1,cmp);
    176     FOR(st,1,n) {
    177         int last=st;
    178         while(last<=n&&dep[pos[last+1]]==dep[pos[st]]&&H[pos[last+1]]==H[pos[st]])
    179             last++;
    180         FOR(i,st,last) FOR(j,st,last) {
    181             int X=pos[i],Y=pos[j],tot=0;
    182             FOR(k,0,(int)g[X].size()-1)
    183                 if(fa[X]!=g[X][k].v&&!g[X][k].flag) tot++;
    184             km.clear(tot);
    185             int idx=1,idy=1;
    186             FOR(k,0,(int)g[X].size()-1) {
    187                 int v=g[X][k].v;
    188                 if(v!=fa[X] && !g[X][k].flag) {
    189                     idy=1;
    190                     FOR(k2,0,(int)g[Y].size()-1) {
    191                         int v2=g[Y][k2].v;
    192                         if(v2!=fa[Y] && !g[Y][k2].flag) {
    193                             km.g[idx][idy]=F[v][v2];
    194                             idy++;
    195                         }
    196                     }
    197                     idx++;
    198                 }
    199             }
    200             F[X][Y]=km.solve(tot);
    201             F[X][Y]+=firstST[X]==finalST[Y]? 0:1;
    202         }
    203         st=last;
    204     }
    205 }
    206 
    207 int main() {
    208     read(n);
    209     int u,v;
    210     FOR(i,1,n-1) {
    211         read(u),read(v);
    212         adde(u,v);
    213     }
    214     FOR(i,1,n) read(firstST[i]);
    215     FOR(i,1,n) read(finalST[i]);
    216     root=getroot();
    217     text(root,-1);
    218     DP();
    219     printf("%d",F[root][root]);
    220     return 0;
    221 }
  • 相关阅读:
    linux学习(三)输入输出重定向和管道功能、cat命令、more命令
    linux基础学习(二)ls命令以及文件访问权限例(-rw-r-r--)
    c语言的全排列
    linux基础学习(一)常用命令:date、pwd、cd、cal、who、wc等等
    用linux编译并运行c文件
    安装linux虚拟机
    安装quickLook插件以及解决如何不能读取offic问题
    java:数据结构(四)二叉查找树以及树的三种遍历
    java:数据结构复习(三)链表队列
    数据结构java学习(三)循环队列
  • 原文地址:https://www.cnblogs.com/lidaxin/p/5237454.html
Copyright © 2011-2022 走看看