LED屏是由一个庞大的点阵小灯泡组成的,一开始每个小灯泡都不发光。每一行一共有N个小灯泡,依次标号为1~n。现在给定K个点,要求这K个点发光,其余点必须保持熄灭状态。而这块LED屏的操作方式各种奇葩,一共有L种操作方法,第i种表示你能将任意长度恰为A_i的连续一段灯泡的状态取反(灭变亮,亮变灭)。
已知LED屏一共有m行,为了节省时间,请你算出每一行达到目标状态所需的最少操作次数。
标签:string 常用 cstring 比较 100% 无法 转移 ace output
题解:一开始想网络流想了半天。。。结果是错的。
第一步很常用也很关键,我们将原序列差分,然后就变成了只有2k个关键点需要染黑,而每个操作可以看成是将两个距离为L的格子同时反色。这个过程比较类似于一个最短路的过程,我们将一个关键点染色,此时出现了一个多余的点,我们再将多余点不断反色直到走到了另一个关键点,此时我们可以看做这两个关键点成功配对,而代价就是从这个点走到另一个点的最短路。所以我们先预处理出任意两个关键点之间的最短路,然后DP。
设f[S]表示已经配对的点的状态为S的最小代价。转移时,我只需要们枚举S中最后那个点和谁配对即可。
#include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <algorithm> using namespace std; int n,m,K,L; bool vis[10010]; int op[30],w[30],v[110],dis[10010],f[30][30],g[1<<20],Log[1<<20]; queue<int> q; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+(gc^‘0‘),gc=getchar(); return ret*f; } inline void work() { n=rd(),K=rd(),L=rd(),m=0; memset(f,0x3f,sizeof(f)),memset(g,0x3f,sizeof(g)); int i,j,a,u; for(i=1;i<=K;i++) { op[i]=rd(); for(j=1;j<i;j++) if(op[j]==op[i]) break; if(j==i) vis[op[i]]^=1,vis[op[i]+1]^=1; } for(i=1;i<=n+1;i++) if(vis[i]) w[m++]=i,vis[i]=0; for(i=1;i<=L;i++) v[i]=rd(); sort(w,w+m); for(i=0;i<m;i++) Log[1<<i]=i; for(i=0;i<m;i++) { memset(dis,0x3f,sizeof(dis)); q.push(w[i]),dis[w[i]]=0; while(!q.empty()) { u=q.front(),q.pop(); for(j=1;j<=L;j++) { if(u+v[j]<=n+1&&dis[u+v[j]]==0x3f3f3f3f) dis[u+v[j]]=dis[u]+1,q.push(u+v[j]); if(u-v[j]>=1&&dis[u-v[j]]==0x3f3f3f3f) dis[u-v[j]]=dis[u]+1,q.push(u-v[j]); } } for(j=0;j<m;j++) if(i!=j) f[i][j]=dis[w[j]]; } g[0]=0; for(i=1;i<(1<<m);i++) { a=Log[i&-i]; for(j=a+1;j<m;j++) if((i>>j)&1) g[i]=min(g[i],g[i^(1<<j)^(1<<a)]+f[j][a]); } printf("%d\n",(g[(1<<m)-1]==0x3f3f3f3f)?-1:g[(1<<m)-1]); } int main() { int T=rd(); while(T--) work(); return 0; }//2 10 8 2 1 2 3 5 6 7 8 9 3 5 3 2 1 1 2 3 //1 10 3 2 1 1 10 10 8
标签:string 常用 cstring 比较 100% 无法 转移 ace output
原文地址:http://www.cnblogs.com/CQzhangyu/p/7965415.html