【NOIP模擬賽】密碼鎖
阿新 • • 發佈:2017-05-21
提示 都是 sin urn 輸入 題目 fin i++ 元素
第1行,三個正整數N,K,M,如題目所述。
第2行,K個正整數,表示開關x1,x2,x3..xk必須為開,保證x兩兩不同。
第三行,M個正整數,表示size[i],size[]可能有重復元素。
題目描述
hzwer有一把密碼鎖,由N個開關組成。一開始的時候,所有開關都是關上的。當且僅當開關x1,x2,x3,…xk為開,其他開關為關時,密碼鎖才會打開。 他可以進行M種的操作,每種操作有一個size[i],表示,假如他選擇了第i種的操作的話,他可以任意選擇連續的size[i]個格子,把它們全部取反。(註意,由於黃金大神非常的神,所以操作次數可以無限>_<) 本來這是一個無關緊要的問題,但是,黃金大神不小心他的錢丟進去了,沒有的錢他哪裏能逃過被chenzeyu97 NTR的命運?>_< 於是,他為了虐爆czy,也為了去泡更多的妹子,決定打開這把鎖。但是他那麽神的人根本不屑這種”水題”。於是,他找到了你。 你的任務很簡單,求出最少需要多少步才能打開密碼鎖,或者如果無解的話,請輸出-1。輸入
輸出
輸出答案,無解輸出-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模擬賽】密碼鎖