链接:

http://exam.upc.edu.cn/problem.php?id=3135


题目:

题目描述

hzwer有一把密码锁,由N个开关组成。一开始的时候,所有开关都是关上的。当且仅当开关x1,x2,x3,...xk为开,其他开关为关时,密码锁才会打开。
他可以进行M种的操作,每种操作有一个size[i],表示,假如他选择了第i种的操作的话,他可以任意选择连续的size[i]个格子,把它们全部取反。(注意,由于黄金大神非常的神,所以操作次数可以无限>_<)
本来这是一个无关紧要的问题,但是,黄金大神不小心他的钱丢进去了,没有的钱他哪里能逃过被chenzeyu97 NTR的命运?>_< 于是,他为了虐爆czy,也为了去泡更多的妹子,决定打开这把锁。但是他那么神的人根本不屑这种”水题”。于是,他找到了你。
你的任务很简单,求出最少需要多少步才能打开密码锁,或者如果无解的话,请输出-1。

输入

第1行,三个正整数N,K,M,如题目所述。
第2行,K个正整数,表示开关x1,x2,x3..xk必须为开,保证x两两不同。
第三行,M个正整数,表示size[i],size[]可能有重复元素。

输出

输出答案,无解输出-1。

样例输入

10 8 2
1 2 3 5 6 7 8 9
3 5

样例输出

2

提示

对于50%的数据,1≤N≤20,1≤k≤5,1≤m≤3;
对于另外20%的数据,1≤N≤10000,1≤k≤5,1≤m≤30;
对于100%的数据,1≤N≤10000,1≤k≤10,1≤m≤100。


思路:

  假设最终状态是:1 1 1 0 1 0
  差分一下:   1 0 0 1 1 1
  四个1分别出现在1,4,5,6四个位置,代表$[1, 4)$和$[5, 6)$这两个区间里的数都是1
  先处理出每一段区间全部变成1所需要的最少操作数,然后我们可以发现,取反这两个区间和取反$[1,5)$,$[4,6)$这两个区间是等价的,所以这些数可以随机两两组合来进行变换,每次变换的加起来就是这种方案的操作数。


实现:

#include <bits/stdc++.h>
const int inf = 0x3f3f3f3f, maxn = int(1e4) + 7, maxm = int(2e6) + 7, cache = 1 << 16;
char buf[cache];
size_t curl, curr;
inline char get() {
    if (curl == curr) { curl = 0, curr = fread(buf, 1, cache, stdin); if (curr == curl) return EOF; }
    return buf[curl++];
}
inline int read(int x = 0, char c = '0') {
    do c = get(); while (!isdigit(c)); x = c ^ '0'; while (isdigit(c = get())) x = x * 10 + (c ^ '0');
    return x;
}
int n, k, m, cnt, dist[maxn], dp[maxm], mp[27][27], a[maxn], sz[maxn], num[maxn];
bool mark[maxn], vis[maxn];
std::queue<int> que;
void bfs(int x, int now = 0) {
    memset(vis, 0, sizeof(vis));
    dist[x] = 0, vis[x] = true;
    que.push(x);
    while (!que.empty()) {
        now = que.front(), que.pop();
        for (int i = 1; i <= m; i++) {
            if (now + sz[i] <= n && (!vis[now + sz[i]])) {
                vis[now + sz[i]] = true;
                dist[now + sz[i]] = dist[now] + 1;
                que.push(now + sz[i]);
            }
            if (now - sz[i] > 0 && (!vis[now - sz[i]])) {
                vis[now - sz[i]] = true;
                dist[now - sz[i]] = dist[now] + 1;
                que.push(now - sz[i]);
            }
        }
    }
    for (int i = 1; i <= n; i++) if (num[i]) mp[num[x]][num[i]] = vis[i] ? dist[i] : inf;
}
int dfs(int x, int st = 0) {
    if (!x) return 0;
    if (~dp[x]) return dp[x];
    dp[x] = inf;
    for (int i = 1; i <= cnt; i++)
        if (x & (1 << (i - 1))) {
            if (!st) st = i;
            else if (mp[st][i] != inf) dp[x] = std::min(dp[x], dfs(x ^ (1 << (st - 1)) ^ (1 << (i - 1))) + mp[st][i]);
        }
    return dp[x];
}
int main() {
//    freopen("in.txt", "r", stdin);
    memset(dp, -1, sizeof(dp));
    n = read(), k = read(), m = read();
    for (int i = 1; i <= k; i++) mark[a[i] = read()] = true;
    for (int i = 1; i <= m; i++) sz[i] = read();
    for (int i = ++n; i; i--) mark[i] ^= mark[i - 1];
    for (int i = 1; i <= n; i++) if (mark[i]) num[i] = ++cnt;
    for (int i = 1; i <= n; i++) if (mark[i]) bfs(i);
    printf("%d\n", dfs((1 << cnt) - 1) == inf ? -1 : dp[(1 << cnt) - 1]);
    return 0;
}
最后修改:2018 年 02 月 21 日
谢谢老板!