1. 程式人生 > >牛客訓練(狀壓DP)

牛客訓練(狀壓DP)

有想到要狀壓可是不知道要用壓縮後的狀態來表示什麼,題還是做太少了。。

設d[S]表示已經選擇S的情況下的最小衝突(即以後再選的優先順序都要比S裡面的都小),這樣轉移的時候只要列舉S的子集,分離成原有的子集V和後加的子集T合併成S這樣來轉移就行了。。

列舉的複雜度計算可以根據集合的size分類計算,所以是O(\sum C_n^k2^{n-k})=O(3^n)

然而轉移的時候計算比較複雜,一方面是T自己和自己優先順序相同的衝突,一個是V和T的衝突,如果暴力列舉複雜度相當高,不過可以先預處理一下,自己和自己衝突的容易處理,然而直接預處理集合之間的衝突需要的複雜度是O(4^n),所以只能求一下一個元素和一個集合之間的衝突,然後轉移的時候列舉一下當前元素的衝突即可。。

因此總複雜度為O(n^22^n+n3^n)

然後看別人程式碼學到了列舉子集的姿勢,得學一下(自己是直接寫搜尋列舉的,比較麻煩)

解法一:

/**
 *        ┏┓    ┏┓
 *        ┏┛┗━━━━━━━┛┗━━━┓
 *        ┃       ┃  
 *        ┃   ━    ┃
 *        ┃ >   < ┃
 *        ┃       ┃
 *        ┃... ⌒ ...  ┃
 *        ┃       ┃
 *        ┗━┓   ┏━┛
 *          ┃   ┃ Code is far away from bug with the animal protecting          
 *          ┃   ┃   神獸保佑,程式碼無bug
 *          ┃   ┃           
 *          ┃   ┃        
 *          ┃   ┃
 *          ┃   ┃           
 *          ┃   ┗━━━┓
 *          ┃       ┣┓
 *          ┃       ┏┛
 *          ┗┓┓┏━┳┓┏┛
 *           ┃┫┫ ┃┫┫
 *           ┗┻┛ ┗┻┛
 */ 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<stdlib.h>
#include<assert.h>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define eps 1e-8
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid (x+y)/2
#define NM 17 
#define nm 40005
#define pi 3.1415926535897931
const int inf=1e9+7;
using namespace std;
ll read(){
    ll x=0,f=1;char ch=getchar() ;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}




int n,tot,p[NM],a[NM][NM],_x,_y,d[nm],b[NM][nm],c[nm];



int main(){
    n=read();tot=succ(n)-1;p[0]=1;
    inc(i,1,n)p[i]=p[i-1]<<1;
    inc(i,1,2*n*(n-1)){_x=read();_y=read();a[_x][_y]++;}
    inc(k,1,tot)inc(i,1,n)if(p[i-1]&k)inc(j,i+1,n)if(p[j-1]&k)c[k]+=abs(a[i][j]-a[j][i]);
    inc(i,1,n)inc(k,1,tot)if(!(k&p[i-1]))inc(j,1,n)if(k&p[j-1])b[i][k]+=a[j][i];
    inc(i,1,tot)d[i]=inf;
    inc(i,1,tot)for(int t=i;t;t=(t-1)&i){
	int s=0;
	inc(j,1,n)if(t&succ(j-1))s+=b[j][i^t];
	d[i]=min(d[i],d[i^t]+s+c[t]);
    }
    return 0*printf("%d\n",d[tot]);
}

解法二:

/**
 *        ┏┓    ┏┓
 *        ┏┛┗━━━━━━━┛┗━━━┓
 *        ┃       ┃  
 *        ┃   ━    ┃
 *        ┃ >   < ┃
 *        ┃       ┃
 *        ┃... ⌒ ...  ┃
 *        ┃       ┃
 *        ┗━┓   ┏━┛
 *          ┃   ┃ Code is far away from bug with the animal protecting          
 *          ┃   ┃   神獸保佑,程式碼無bug
 *          ┃   ┃           
 *          ┃   ┃        
 *          ┃   ┃
 *          ┃   ┃           
 *          ┃   ┗━━━┓
 *          ┃       ┣┓
 *          ┃       ┏┛
 *          ┗┓┓┏━┳┓┏┛
 *           ┃┫┫ ┃┫┫
 *           ┗┻┛ ┗┻┛
 */ 
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<stdlib.h>
#include<assert.h>
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,l,r) for(int i=l;i>=r;i--)
#define link(x) for(edge *j=h[x];j;j=j->next)
#define mem(a) memset(a,0,sizeof(a))
#define ll long long
#define eps 1e-8
#define succ(x) (1<<x)
#define lowbit(x) (x&(-x))
#define sqr(x) ((x)*(x))
#define mid (x+y)/2
#define NM 17 
#define nm 40005
#define pi 3.1415926535897931
const int inf=1e9+7;
using namespace std;
ll read(){
    ll x=0,f=1;char ch=getchar() ;
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
    return f*x;
}




int n,tot,p[NM],a[NM][NM],_x,_y,d[nm],b[NM][nm],c[nm],tmp,cnt;

void cal(){
    int t=tmp|cnt;int s=c[cnt]+d[tmp];
    inc(i,1,n)if(cnt&p[i-1])s+=b[i][tmp];
    d[t]=min(d[t],s);
}

void dfs(int x){
    if(x==n+1){cal();return;}
    dfs(x+1);if(!(tmp&p[x-1])){cnt^=p[x-1];dfs(x+1);cnt^=p[x-1];}
}

int main(){
    n=read();tot=succ(n)-1;p[0]=1;
    inc(i,1,n)p[i]=p[i-1]<<1;
    inc(i,1,2*n*(n-1)){_x=read();_y=read();a[_x][_y]++;}
    inc(k,1,tot)inc(i,1,n)if(p[i-1]&k)inc(j,i+1,n)if(p[j-1]&k)c[k]+=abs(a[i][j]-a[j][i]);
    inc(i,1,n)inc(k,1,tot)if(!(k&p[i-1]))inc(j,1,n)if(k&p[j-1])b[i][k]+=a[j][i];
    inc(i,1,tot)d[i]=inf;
    for(tmp=0;tmp<tot;tmp++)dfs(1);
    return 0*printf("%d\n",d[tot]);
}

資料排序

時間限制:C/C++ 1秒,其他語言2秒 空間限制:C/C++ 1048576K,其他語言2097152K 64bit IO Format: %lld

題目描述

機器學習通常需要用到大量的人工標註好的資料進行訓練。現在有這麼一個數據集,有 N 個張照片,每張照片中都有一個模特。某個研究員想要訓練一個機器學習演算法,能夠根據照片對模特的魅力值進行評分。為了完成這個演算法,研究員找了若干個志願者對資料做一個標註。每個志願者每次會看到系統給出的兩張照片 x 和 y,然後告訴系統他認為哪張照片的魅力值更高。例如 x 的魅力值比 y 的要高(記作 <x, y>)這樣一個有序二元組稱之為一個數標註。 研究員收集了若干個這樣的資料標註,他想找到一組對每張照片的評分 c1, ..., cn,使得這個評分和資料的衝突越少越好。為了方便設定N 張照片所組成的 對照片都分別有 4 個記錄,也就是被標註了 4 次。定義 g(x, y) 為記錄<x, y>出現的次數,定義評分 {cn} 的衝突值: 你需要求出在這個資料集下衝突值 f(c) 的最小值。

輸入描述:

第一行一個整數 N,表示資料集大小。
接下來 2N(N-1) 行,每一行都有兩個整數 xi, yi ,表示第 i 組資料標註 < xi, yi >.

輸出描述:

輸出一個整數,衝突值的最小值。

示例1

輸入

複製

2
1 2
1 2
2 1
1 2

輸出

複製

1

說明

如果這兩個資料得分相同,則衝突值為2;如果 1 比 2 得分高,則衝突值為1;如果 2 比 1 得分高,則衝突值為 3. 所以衝突值的最小值為1.

備註:

1 ≤ N ≤ 15, 1 ≤ xi,yi ≤ N, xi ≠ yi