2019牛客国庆day3-G &CF1238E
给定大小为N的数组a[],给定M组关系,让你重排a[],使得sum{M队关系的绝对值之差}最小。首先将a排序,然后依次把a填入数组。
假设i在二进制下有x个1,用dp[i]更新dp[i|(1<<j)],表示的是,将a[x+1]填在第j个位置。注意到a[]已经排序了,那么a[x]的贡献就是:+之前填的个数*a[x]-没填的个数*a[x];
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int N=20; int a[N],p[N],e[N]; ll f[1<<N]; int main(){ int n,m; while(scanf("%d%d",&n,&m)!=EOF) { for(int i=0;i<n;i++) scanf("%d",&a[i]); sort(a,a+n); //重排,然后依次填入 for(int i=0;i<n;i++) { e[i]=0; p[i]=0; } for(int i=0;i<m;i++) { int a,b; scanf("%d%d",&a,&b); a--;b--; p[a]|=1<<b;p[b]|=1<<a; e[a]++,e[b]++; } f[0]=0; for(int i=1;i<(1<<n);i++) { f[i]=1e12; int k=__builtin_popcount(i)-1; //表示这一轮填a[k] for(int j=0;j<n;j++) { if(i&(1<<j)) {//表示a[k]填在posj处 f[i]=min(f[i],f[i^(1<<j)]+1LL*a[k]*(__builtin_popcount(p[j]&i)*2-e[j])); } } } printf("%lld\n",f[(1<<n)-1]); } return 0; }
题意:给定M队关系(ai,bi),让你重排,使得{关系的位置绝对值之和}最小。
依然是枚举最后一个填谁,那么这一轮的贡献是满足(ai已经填入,bi未填入)的个数。
#include<bits/stdc++.h> #define rep(i,a,b) for(int i=a;i<=b;i++) using namespace std; const int maxn=2000010; char c[maxn]; int dp[maxn],f[30][30]; int main() { int N,M; scanf("%d%d%s",&N,&M,c+1); memset(dp,0x3f,sizeof(dp)); rep(i,1,N-1) f[c[i]-'a'][c[i+1]-'a']++,f[c[i+1]-'a'][c[i]-'a']++; dp[0]=0; rep(i,0,(1<<M)-1){ int t=0; rep(j,0,M-1) { if(i&(1<<j)){ rep(k,0,M-1) if(!(i&(1<<k))) t+=f[j][k]; } } rep(j,0,M-1) if(!(i&(1<<j))) dp[i|(1<<j)]=min(dp[i|(1<<j)],dp[i]+t); } printf("%d\n",dp[(1<<M)-1]); return 0; }
It is your time to fight!