Codeforces Round #599 (Div. 1) C. Sum Balance

題目鏈接

題目大意:

kk個箱子,箱子iinin_i個數字,能否滿足:
在每個箱子取一個數字,一共取出k個數字。
再把這k個數字重新分配給每個箱子,使得每個箱子的數字總和相等。
如果不能滿足,輸出no,如果可以滿足,輸出yes並打印方案(每個箱子取出哪個數字,放到哪個箱子)。
(k15,n5000,ai1e9)(k\le 15,n \le 5000, |a_i| \le 1e9)保證所有數字兩兩不同

解題思路:

容易知道,如果可以滿足,那麼最終每個箱子的總和爲sum/ksum/k,其中sum=aisum = \sum a_i.那麼對於每個箱子的數字,可以直接算出如果從這個箱子取出這個數字,那麼應該再放入的數字的值。那麼可以建一個有向圖:數字aia_i如果取出,算出來需要放入aja_j。那麼加有向邊ai>aja_i->a_j。這樣如果找到一個環的集合,使得覆蓋每個容器恰好一次,那麼就可以構建出答案。
因爲每個點只有一個出度,所以可以直接枚舉起點找環。找到一個覆蓋部分容器剛好一次的環,就記錄下來。這樣我們得到了所有覆蓋某些容器恰好一次的環。
然後我們利用子集DP獲取恰好覆蓋所有容器的環的集合。複雜度O(3k)O(3^k)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 3e5 + 50;
const int inf = 0x3f3f3f3f;
map<int, int> mp;
struct node{
    vector<int> ned;
}q[maxn];
int tot = 0, n, a[maxn], cnt = 0;
ll sum;
ll D[16];
ll to(int x){
    int id = mp[x];
    return sum - (D[id]-x);
}
int dp[1<<16];
vector<int> path;
void dfs(ll x, int mask){
    if(x > inf) return;
    if(mp.find(x) == mp.end()) return;
    int id = mp[x];
    if((mask>>id)&1){//出現過
        int st = -1;
        for(int i = 0; i < path.size(); ++i){
            if(path[i] == x) st = i;
        }
        if(st == -1) return;//無環
        for(int i = 0; i < st; ++i) mask ^= (1<<mp[path[i]]);
        if(dp[mask]) return;//此環出現過
        ++tot;
        dp[mask] = -tot;
        for(int i = st; i < path.size(); ++i){
            q[tot].ned.push_back(path[i]);
        }
        return;
    }
    path.push_back(x);
    dfs(to(x), mask|(1<<id));
}
vector<int> ans;
void go(int mask){
    if(dp[mask] < 0){
        int id = -dp[mask];
        for(int i = 0; i < q[id].ned.size(); ++i){
            ans.push_back(q[id].ned[i]);
        }return;
    }
    go(dp[mask]); go(mask^dp[mask]);
}
int main()
{
	cin>>n;
	sum = 0;
	for(int i = 0; i < n; ++i){
        int k; scanf("%d", &k);
        while(k--){
            scanf("%d", &a[cnt]);
            mp[a[cnt]] = i;
            sum += a[cnt];
            D[i] += a[cnt];
            cnt++;
        }
	}
	if(sum%n != 0){
        printf("No\n"); return 0;
	}
	sum /= n;
	for(int i = 0; i < cnt; ++i){
        path.clear();
        dfs(a[i], 0);
	}
	for(int mask = 1; mask < (1<<n); ++mask){
        if(dp[mask]) continue;
        for(int sub = (mask-1)&mask; sub; sub = (sub-1)&mask){
            if(dp[sub] && dp[mask^sub]){

                dp[mask] = sub;
                break;
            }
        }
	}
	if(!dp[(1<<n)-1]) cout<<"No"<<endl;
	else{
        cout<<"Yes"<<endl;
        go((1<<n)-1);
        int pre[15], val[15];
        for(int i = 0; i < ans.size(); ++i){
            int x = ans[i];
            int id = mp[x];
            int v = to(x);
            val[id] = x;
            pre[mp[v]] = id;
        }
        for(int i = 0; i < n; ++i){
            cout<<val[i]<<" "<<pre[i]+1<<endl;
        }
	}
	//for(int i = 0; i < (1<<n); ++i) cout<<"i:"<<i<<" dp:"<<dp[i]<<endl;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章