• codeforces 1215 E Marbles-----状压DP


    Marbles

    题意:长度4e5的数字序列,不同数字个数至多20个,每次操作可选取两个相邻数字并交换位置,现在想要使序列中所有相同数字都排列在一起,问至少需要几次操作。时限4s.

    题解:由不同数字个数至多20应该想到状压DP......

    ​ 现在假想全部排完之后的状态,根据不同数字块的位置显然有(20!)种情况,那么我们可以假想数字1到20是一个一个完成合并并放到数列左端的。这样的话(DP[i])就表示(i)数位上为(1)的数字完成合并并丢在左边所需操作次数。

    ​ 预处理一个(val[i][j])表示把所有数字(i)放到所有数字(j)左边所需操作次数,那么很显然转移的增量就是这个数字与其他所有未完成合并的数字的(val)和。(好巧妙TuT)

    ​ 复杂度:o((2^{20}*400)).

    #include <bits/stdc++.h>
    using namespace std;
    int n;
    vector<int> pos[20];
    typedef long long ll;
    ll dp[1<<20],val[20][20];
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		int x;
    		scanf("%d",&x);
    		pos[x-1].push_back(i);
    	}
    	for(int i=0;i<20;i++){
    		for(int j=0;j<20;j++){
    			if(i==j||pos[j].empty()) continue;
    			for(int k=0;k<int(pos[i].size());k++){
    				int x=pos[i][k];
    				val[i][j]+=lower_bound(pos[j].begin(),pos[j].end(),x)-pos[j].begin();
    			}
    		}
    	}
    	memset(dp,0x3f3f3f3f,sizeof(dp));
    	dp[0]=0;
    	for(int i=0;i<(1<<20);i++){
    		vector<int> u;
    		for(int j=0;j<20;j++){
    			if((1<<j)&i) continue;
    			u.push_back(j);
    		}
    		for(int j=0;j<int(u.size());j++){
    			int x=u[j];
    			ll sum=0;
    			for(int k=0;k<int(u.size());k++){
    				if(u[k]!=x) sum+=val[x][u[k]];
    			}
    			dp[i|(1<<x)]=min(dp[i|(1<<x)],dp[i]+sum);
    		}
    	}
    	cout << dp[(1<<20)-1];
    	return 0;
    }
    
  • 相关阅读:
    关于ListView的注意点
    推荐一波 瀑布流的RecylceView
    RecycleView的简单应用
    Java Junit单元测试
    Java 2 个 List 集合数据求并、补集操作
    Java @Validated 遇到的大坑
    Java中的Validated验证
    使用一条sql语句查询多表的总数
    Java thymeleaf模板获取资源文件的内容
    Java热启动
  • 原文地址:https://www.cnblogs.com/vege-chicken-rainstar/p/11526722.html
Copyright © 2020-2023  润新知