返回顶部

Educational Codeforces Round 109 (Rated for Div. 2) D. Armchairs (dp)

    • 题意:有\(n\)个座位,刚开始有\(k\ (k\le \frac{n}{2})\)个人坐着,你可以让某个人\(i\)移动到空的座位\(j\),花费\(|i-j|\),问你最少花多少使得刚开始坐着人的位置全部空出来.

    • 题解:首先想了一下,这题必然不能贪心,\(0\)对于某两个\(1\)来说距离相等的话,状态就不确定.那么就只能考虑dp,用两个数组分别存\(1\)\(0\)的位置,枚举\(1\)\(0\)进行转移,\(dp[i][j]\)表示前\(i\)\(1\)和前\(j\)\(0\)中的最优解,对于第\(i\)\(1\),假如我们要挪到\(j\)这个位置,那么肯定从\(dp[i-1][k](k\in[0,j-1])\)转移过来,这样的话我们用了三个循环,\(O(n^3)\)直接起飞,但是不难发现,我们要取的\(dp[i-1][k]\)其实是上一个状态的最小值,那么每次转移完后,维护一下前缀的最小值,这样转移的时候就可以\(O(1)\),即\(dp[i][j]=min(dp[i][j],dp[i-1][j-1]+abs(pos_1-pos_2))\).

    • 代码:

      #include <bits/stdc++.h>
      #define ll long long
      #define fi first
      #define se second
      #define pb push_back
      #define me memset
      #define rep(a,b,c) for(int a=b;a<=c;++a)
      #define per(a,b,c) for(int a=b;a>=c;--a)
      const int N = 1e6 + 10;
      const int mod = 1e9 + 7;
      const int INF = 0x3f3f3f3f;
      using namespace std;
      typedef pair<int,int> PII;
      typedef pair<ll,ll> PLL;
      ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
      ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
       
      int n;
      int a[N];
      vector<int> _0,_1;
      int dp[5005][5005];  //前i个1和前j个0的最优方案
       
      int main() {
          ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
      	cin>>n;
      	_1.pb(-1),_0.pb(-1);
       
      	rep(i,1,n){
      		cin>>a[i];
      		if(a[i]==1) _1.pb(i);
      		else _0.pb(i);
      	}
      	rep(i,1,(int)_1.size()-1){
      		rep(j,0,(int)_0.size()-1){
      			dp[i][j]=INF;
      		}
      	}
       
      	rep(i,1,(int)_1.size()-1){
      		rep(j,1,(int)_0.size()-1){  //0和1都是递增
      			dp[i][j]=min(dp[i][j],dp[i-1][j-1]+abs(_1[i]-_0[j])); //j-1表示前缀最小
      		}
      		rep(j,1,(int)_0.size()-1) dp[i][j]=min(dp[i][j],dp[i][j-1]); //更新前缀
      	}	
       
      	cout<<dp[(int)_1.size()-1][(int)_0.size()-1]<<'\n';
       
          return 0;
      }
      
  • 附:反悔贪心法

    int n;
    int a[N];
    priority_queue<ll,vector<ll>,greater<ll>> h0,h1;
     
    int main(){
    	scanf("%d",&n);
     
    	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    	ll ans=0;
    	for(int i=1;i<=n;++i){
    		if(a[i]){
    			ll reg=1e15;
    			if(!h0.empty()){
    				ll x=h0.top();
    				h0.pop();
    				reg=i+x;
    			}
    			ans+=reg;
    			h1.push(-i-reg);
    		}
    		else{
    			if(!h1.empty() && i+h1.top()<0){
    				ll reg=i+h1.top();
    				h1.pop();
    				ans+=reg;
    				h0.push(-i-reg);
    			}
    			else{
    				h0.push(-i);
    			}
    		}
    	}
    	
    	printf("%lld\n",ans);
     
    	return 0;
    }
    
posted @ 2021-05-18 13:34  _Kolibri  阅读(57)  评论(0)    收藏  举报