Topcoder SRM 549 DIV2 1000 OrderOfTheHats


Task:
孩子们有 N (1<=N<=20)个音标要去学,按照一般的教学法则,每次学一个音标的时候,要先把比这个音标更基础的音标学会。但由于这个教学法则会出现循环需要的情况导致孩子们无法学完,请你帮助他们用最少的次数改变这个教学法则。
给定一个 vector < string > spellChart ,其中 spellChart[i][j] 表示如果为 Y 则音标 i 必须在j前学习完,否则为 N 。你可以将 Y 改成 N 或者 N 改成 Y ,但每个位置只能改变一次。输出最少次数。


Solution:
这个题目可以运用构图的思想:

给出一个有N个点的有向图,其中可能含有环。我们可以有以下两个操作:选择两个没有直接相连的点u,v连接,或者选择直接相连的点,删除边 (u,v) 。求出最小要改变的次数,使得新图中没有环。

再继续分析题目,可以发现的是:永远都不需要添加边,因为添加边的操作永远不可能会减少当前图中环的个数。那么题目也就变为如何删除最少的边,使图转化为DAG?那么我们就会想到DAG的性质:拓扑序。(然而我想到了Toposort

对于一段拓扑序 T={t[0],t[1],,t[i],,t[j],,t[n1]} ,必然不包含有向边 (t[j],t[i]) ,否则 t[j] 就不会排在 t[i] 的后面。所以对于任何可能的拓扑序 T ,我们必须删除掉的反向边的最小个数就是所求的答案。

如果考虑枚举这个拓扑序,O(N!)的复杂度尽管 N 很小但也是吃不消的。不妨抽出其中的一个子状态Set来看,可以发现:当你去更新其它不含于Set内的点时,你要删除的边只会跟 Set 内元素与spellChart[x]内的元素的重复个数有关,即有多少元素需要先学而当前状态已经学掉了。

Set 内元素是不会发生改变的,导致 O(N!) 的复杂度的是Set内点进行了全排列 Acntcnt 。这个全排列是不必要的,因为我们只需要当状态为 Set 最大值。根据这句话,我们可以采用状压dp求解:

定义 dp[Set] 为当前点集为Set的最大值,则

dp[Set]=minx=U{calc(SetMark[x])+dp[Set+2x]}

显然从 O(N!) 优化到了 O(2N) ,那么 N<=20 的范围也可以过了。
还要提一下的是这个题目运用了很多的二进制操作去优化中间的代码与时间复杂度。

class OrderOfTheHats {
public:
    static const int inf=1<<30;
    int All_mark,n;
    int vis[20],dp[1<<20];
    inline void Min(int &a,int b){if(a>b)a=b;}
    int calc(int m){
        int cnt=0;
        while(m){m-=m&(-m);++cnt;}
        return cnt;
    }//计算目前状态去学下一个音标产生矛盾的个数。
    int rec(int mark){
        if(mark==All_mark)return 0;
        int &ans=dp[mark];
        if(~ans)return ans;
        ans=inf;
        for(int i=0;i<n;i++)
            if(!(mark>>i&1)){
                int nxt=mark|(1<<i);
                Min(ans,calc(nxt&vis[i])+rec(nxt));
            }
        return ans;
    }
    int minChanged(vector<string> str){
        n=str.size();
        All_mark=(1<<n)-1;
        for(int i=0;i<n;i++){
            vis[i]=0;
            for(int j=0;j<n;j++)
                if(str[i][j]=='Y')vis[i]|=1<<j;
        }
        memset(dp,-1,sizeof(dp));
        return rec(0);
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值