星空[好题][题意转化]
題目大意:給你一個0/1串,讓你對區(qū)間亦或(有特定幾個長度限制操作),最后讓你求出把這個0/1串變成全0串的最小操作數(shù);
前置問題:
首先發(fā)現(xiàn)對原序列區(qū)間整體亦或很不好控制,因為會不斷出現(xiàn)新的1;
那我們怎么辦?
想想差分;
與普通線性數(shù)組差分一樣,若原序列有初始值,則需要把原序列轉(zhuǎn)化為差分序列;
具體求法即為:b[i]=a[i]-a[i-1];(b數(shù)組為差分數(shù)組,a為原數(shù)組)
同理亦或差分:b[i]=a[i]^a[i-1];
于是我們可以把原序列轉(zhuǎn)化成為差分序列;
那我們?yōu)槭裁匆言蛄修D(zhuǎn)化為差分序列呢?
{
1.考慮區(qū)間修改,元序列有0與1相互轉(zhuǎn)化,并會蹦出新的1;
2.而差分數(shù)組只對兩端進行修改;只有0/1的單次轉(zhuǎn)化;
3.最重要的性質(zhì),差分數(shù)組與原數(shù)組的最終狀態(tài)一直,都是全0串;(若 原序列為000000000 ,亦或差分序列不也得是00000000嗎)
}
一-------于是成功轉(zhuǎn)化題意;
在原序列的差分序列上進行如下操作:
1.把可把1在序列上向左移動或向右移動幾種固定長度,當1與1想碰時變?yōu)?;
2.求最終把這個序列變?yōu)槿?串的最小操作數(shù);
eg.差分序列:000110001100?? 區(qū)間修改就是選區(qū)間的左右端點(不嚴謹),然后對兩個端點亦或,這不就是把左端點1亦或成0,而右端點0亦或成1嗎;其實就是把1移動......
那這樣就很好做了;
既然有固定的長度;那就把長度看成邊;
把差分序列上的每個點都和他能一次操作到達的點連邊;而邊權(quán)就是1;一部操作嗎.......
二-------于是就形成了一個圖;
而我們要知道的就是1之間互相到達的代價;在圖中轉(zhuǎn)化為距離;
那就跑dijistrla唄;只不過得跑2k次;
但沒關(guān)系啊;NlogN*2k完全可以;
一波dijistrla后,我們求出了dis[i][j] 代表的是差分序列上 第i個1 到 第j個1的 代價;
三-------于是問題又轉(zhuǎn)化了:
你有2k個物品,兩兩能以一定代價互相匹配,求把這2k個物品分為k對的最小代價;
而k<=8;2k<=16;
狀壓唄;O((k^2)*(2^2k))可以卡過;
最終狀態(tài)就是(1<<2k)-1;
附上自帶常數(shù)的代碼
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define rd(x) scanf("%lld",&x) 4 #define int long long 5 int a[400050],b[400050],edge[1000],pos[100],id[400050]; 6 int N,K,M; 7 int dis[100][100]; 8 int f[1<<20|1],g[100][100]; 9 vector<int>to[400500]; 10 11 struct node{ 12 int to,dis; 13 node(int a,int b){to=a;dis=b;} 14 friend bool operator<(node a,node b){ 15 return a.dis>b.dis; 16 } 17 }; 18 19 bool v[40050]; 20 int d[40050]; 21 void diji(int st){ 22 priority_queue<node>q; 23 memset(d,0x3f,sizeof(d));memset(v,0,sizeof(v)); 24 d[st]=0;q.push(node(st,d[st])); 25 register int u,w; 26 while(q.size()){ 27 u=q.top().to;w=q.top().dis;q.pop(); 28 if(v[u])continue; 29 v[u]=1; 30 for(register int i=0,y;i<to[u].size();++i){ 31 y=to[u][i]; 32 if(v[y])continue; 33 if(d[u]+1<d[y]){ 34 d[y]=d[u]+1; 35 q.push(node(y,d[y])); 36 } 37 } 38 } 39 for(int i=1;i<=pos[0];++i) 40 if(id[i]!=st){ 41 dis[id[st]][i]=dis[i][id[st]]=d[pos[i]]; 42 } 43 } 44 45 signed main(){ 46 rd(N);rd(K);rd(M); 47 for(register int i=1,p;i<=K;++i){ 48 rd(p);a[p]=1; 49 } 50 for(register int i=1;i<=M;++i){ 51 rd(edge[i]); 52 } 53 for(register int i=1;i<=N;++i){ 54 for(register int j=1;j<=M;++j){ 55 if(i+edge[j]>N+1)break; 56 to[i].push_back(i+edge[j]); 57 to[i+edge[j]].push_back(i); 58 } 59 } 60 for(register int i=1;i<=N+1;++i){ 61 b[i]=a[i]^a[i-1]; 62 if(b[i])pos[++pos[0]]=i,id[i]=pos[0]; 63 } 64 for(register int i=1;i<=pos[0]-1;++i)diji(pos[i]); 65 /*for(int i=1;i<=pos[0];++i){ 66 for(int j=1;j<=pos[0];++j)cout<<dis[i][j]<<" "; 67 cout<<endl; 68 }*/ 69 for(register int i=1;i<=pos[0];++i) 70 for(register int j=1;j<=pos[0];++j) 71 if(i!=j)g[i][j]=(1<<i-1)|(1<<j-1); 72 memset(f,0x3f,sizeof(f));register int Z=(1<<pos[0])-1; 73 f[0]=0; 74 for(register int i=0;i<=Z;++i) 75 { 76 if(i==Z)break; 77 for(register int j=1;j<=pos[0];++j) 78 if((i&(1<<j-1))==0){ 79 for(register int k=j+1;k<=pos[0];++k) 80 if((i&(1<<k-1))==0){ 81 f[i|g[j][k]]=min(f[i|g[j][k]],f[i]+dis[j][k]); 82 } 83 } 84 85 } 86 printf("%lld\n",f[Z]); 87 } 自帶常數(shù)爆炸?
轉(zhuǎn)載于:https://www.cnblogs.com/wang-hesong/p/11335472.html
總結(jié)
以上是生活随笔為你收集整理的星空[好题][题意转化]的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PHP 的一些开发规范
- 下一篇: map的实现