zoukankan      html  css  js  c++  java
  • 星空Password

    cf链接

    洛谷链接

    这个题……首先要记清楚亮着的灯是1,不亮的是0

    然后一改改一排嘛所以肯定要差分的

    因为是01串所以每个位置上只能是0或者1(废话嘛

    如果构造一个差分数列分别等于相邻两位是否一样

    那么一个数前面每一位的“一不一样”就可以表示这一位和第一位一不一样

    只要知道第一位是0还是1

    也就间接地知道了这一位是多少

    这个一不一样可以通过异或操作来实现

    前面每一位一不一样可以求个异或前缀和

    两次不一样就一样了嘛

    为了方便记录

    我们令b[i]=a[i]^a[i+1]

    a是输入的

    然后增加一位b[0]

    假装在最前面的小灯泡前面还有一个小灯泡

    作为上面说的已经知道了的第一位

    感觉上这一位是0还是1都无所谓

    实际上如果是0的话很难处理

    我太菜了定为0写不出来

    姑且定为1吧

    那么终止状态也就是差分数组全是0

    然后把翻转操作对应到差分数组上

    如果说翻转i~j吧(改变j-i+1个灯泡)

    那么在差分数组上实际改变的值是i-1和j(间隔j-i+1)

    它们都异或了1

    为了达到全0串我们不会选择i-1和j都是0的异或

    只会选择全为1或者一个为1一个为0的

    可以发现前一种会消除两个1而后一种1只是挪了个位置

    这里的思路其实有点像经典神题独木桥

    一次消除两个的话……万一是奇数个1怎么办?

    这时候再次为了方便起见

    在所有小灯泡后面也添一个亮着的小灯泡

    差分数组的范围为从0~n

    其中第n项等于最后一个真实的小灯泡异或上人为添加的小灯泡

    因为最前最后两个人为添加的小灯泡都是亮着的

    所以差分数组里的1肯定是偶数个

    继续

    那么这样的消除和挪位置不是任意长度都可以的

    只有两个点间隔距离正好等于操作长度时才可以(见上文括号

    可以通过bfs处理出每个1可以挪到哪里,需要多少步

    另外消除两个1当然可以看成把1挪到合适距离消除

    然而更好的做法是看成一个1挪到另一个1的位置上(消消乐

    整理一下,现在的题目已经转化成

    一个01串,其中有偶数个1且部分1可以消耗一定的步数两两消除

    因为1的个数很小,可以直接状压dp

    压成一个数,在这个数的二进制表示上从左到右数第i位

    表示初始位置在第i个的1有没有被消除

    #include<bits/stdc++.h>
    using namespace std;
    struct node{
    	int to,nxt;
    }road[3000000];
    int cnt=0,head[70000],dis[30][70000],op[200],n,m,k;
    int read()
    {
    	int x=0;
    	char ch=getchar();
    	while(ch<'0'||ch>'9') ch=getchar();
    	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    	return x;
    }
    void add(int u,int v)
    {
    	road[++cnt].to=v;
    	road[cnt].nxt=head[u];
    	head[u]=cnt;
    }
    queue<int>q;
    void bfs(int st,int stn)
    {
    	q.push(st);
    	dis[stn][st]=0;
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();
    		for(int i=1;i<=m;i++)
    		{
    			int lst=u-op[i],nxt=u+op[i];
    			if(lst>=0&&dis[stn][u]+1<dis[stn][lst]) 
    			{
    				q.push(lst);
    				dis[stn][lst]=dis[stn][u]+1;
    			}
    			if(nxt<=n&&dis[stn][u]+1<dis[stn][nxt]) 
    			{
    				q.push(nxt);
    				dis[stn][nxt]=dis[stn][u]+1;
    			}
    		}
    	}
    }
    int dp[4000000],pos[100],x[100],a[70000],b[70000],pcnt=0;
    int solve()
    {
    	int st=(1<<pcnt)-1;
    	dp[st]=0;
    	
    	for(int i=st-1;i>=0;i--)
    	      for(int j=0;j<pcnt;j++)
    		      if(!((1<<j)&i)) //注意这个条件
    			      for(int jj=0;jj<j;jj++)
    				      if(!((1<<jj)&i)) dp[i]=min(dp[i],dp[i|(1<<j)|(1<<jj)]+dis[j+1][pos[jj+1]]);
    	
    	return dp[0];
    }
    int main()
    {
    	n=read(),k=read(),m=read();
    	for(int i=0;i<=n+1;i++) a[i]=1;
    	for(int i=1;i<=k;i++) 
    	{
    		x[i]=read();
    		a[x[i]]=0;
    	}
    	for(int i=0;i<=n;i++) 
    	{
    		b[i]=a[i]^a[i+1];
    		if(b[i]) pos[++pcnt]=i;
    	}
    	for(int i=1;i<=m;i++) op[i]=read();
    	for(int i=1;i<=n;i++)
    	for(int j=0;j<=m;j++)
    	{
    		if(i+op[j]+1<=n) 
    		{
    			add(i,i+op[j]);
    			add(i+op[j],i);
    		}
    	}
    	memset(dis,0x3f,sizeof(dis));
    	for(int i=1;i<=pcnt;i++) 
    	bfs(pos[i],i);//这个东西的复杂度是O(nm)的
    	memset(dp,0x3f,sizeof(dp));
    	int ans=solve();
    	if(ans>=0x3f3f3f3f)cout<<"-1";
    	else cout<<ans;
    	return 0;
    }
    
  • 相关阅读:
    close 不弹出对话框
    ASP.NET会话(Session)保存模式
    ASP.NET页面刷新后滚动条保留在刷新前的位置 MaintainScrollPositionOnPostback
    CSS实现垂直居中的5种方法
    ExpandStackTrace
    HttpTunnel
    PropertyAccess类 Linq.Expressions 实现
    DatagramResolver
    AsyncUdpClient 类
    C# LockFreeStack类
  • 原文地址:https://www.cnblogs.com/qwq-/p/14056604.html
Copyright © 2011-2022 走看看