1. 程式人生 > >【NOIP模擬賽】密碼鎖

【NOIP模擬賽】密碼鎖

提示 都是 sin urn 輸入 題目 fin i++ 元素

題目描述

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

提示

【樣例輸入2】 3 2 1 1 2 3 【樣例輸出2】 -1 【數據規模】 對於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 1 1 1 1 0 0

差分後為   1 0 0 1 1 0 0 0 0 1 0

這個差分結果可以換成括號序列的思想來理解

四個1分別出現在1 4 5 10四個位置,這是一個半閉半開區間,也就是說[1,4) [5,10)這兩個區間內的數都必須是1

怎麽辦。

我們先處理出每一段區間全部變成1所需要的最少操作數

初始時所有的位置都是0,所以我們的任務是讓[1,4) [5,10)這兩個區間內的數變成1,而且操作數最少

所以考慮所有讓這兩個內的數變成1的情況,算出每種情況的操作數,然後取最小

把[1,4) [5,10)這兩個區間內的數變成1就是把這兩個區間內的元素取反

我們發現,取反這兩個區間和取反[1,6) [4,10)這兩個區間是等價的,所以這些數可以隨機兩兩組合來進行變換,每次變換的加起來就是這種方案的操作數

k辣麽小很明顯用到狀態壓縮

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
#define inf 1000000000
#define N 10005
#define M 2000005
#define T 45
queue<int>q;
int n,k,m,num[N],x[N],sz[N],a[N],cnt,dis[N],d[30][30],f[M];
bool vis[N],mark[M];
void bfs(int x){
    while(!q.empty())q.pop();
    memset(vis,0,sizeof(vis));
    q.push(x);
    vis[x]=1;dis[x]=0;
    while(!q.empty()){
        int now=q.front();q.pop();
        for(int i=1;i<=m;i++){
            if(now+sz[i]<=n&&(!vis[now+sz[i]])){
                vis[now+sz[i]]=1;
                dis[now+sz[i]]=dis[now]+1;
                q.push(now+sz[i]);
            }
            if(now-sz[i]>0&&(!vis[now-sz[i]])){
                vis[now-sz[i]]=1;
                dis[now-sz[i]]=dis[now]+1;
                q.push(now-sz[i]);
            }
        }
    }
    for(int i=1;i<=n;i++)
        if(num[i]){
            if(!vis[i])d[num[x]][num[i]]=inf;
            else d[num[x]][num[i]]=dis[i];
        }
}
int dp(int x){
    if(!x)return 0;
    if(mark[x])return f[x];
    mark[x]=1;f[x]=inf;
    int st=0;
    for(int i=1;i<=cnt;i++){
        if(x&(1<<(i-1))){
            if(!st)st=i;
            else{
                if(d[st][i]!=inf)
                f[x]=min(f[x],dp(x^(1<<(st-1))^(1<<(i-1)))+d[st][i]);
            }
        }
    }
    return f[x];
}
int main(){
    freopen("password.in","r",stdin);
    freopen("password.out","w",stdout);
    //freopen("Cola.txt","r",stdin);
    scanf("%d%d%d",&n,&k,&m);
    for(int i=1;i<=k;i++){scanf("%d",&x[i]);a[x[i]]=1;}
    for(int i=1;i<=m;i++)scanf("%d",&sz[i]);
    for(int i=n+1;i;i--)a[i]^=a[i-1];
    n++;
    for(int i=1;i<=n;i++){if(a[i])cnt++,num[i]=cnt;}
    for(int i=1;i<=n;i++)if(a[i])bfs(i);
    dp((1<<cnt)-1);
    if(f[(1<<cnt)-1]==inf)printf("-1");
    else printf("%d",f[(1<<cnt)-1]);
    return 0;
}

【NOIP模擬賽】密碼鎖