Codeforces lucky country dp+优化

题目链接

  将原图跑一遍dfs找出每个联通块的的数量a[i],于是就能得到一个很明显的dp方程

  dp[i][j]=min(dp[i-1][j-a[i]])+1

  但复杂度是O(n²),过不了

  于是我们可以想到优化

   很明显,当数据量很大的时候一定会有大量的重复数据,所以我们可以考虑b[x]=∑(a[i]==x),之后就可以用二进制分解来优化

  

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string>
#include<cstring> 
#include<cassert>
#include<cmath>
#include<sstream>
#include<fstream>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<stack>
#include<bitset>
using namespace std;
int lucky[105],lcnt;
void init()
{
    queue<string> q;
    q.push("4");q.push("7");
    while(!q.empty())
    {
        string tmp=q.front();
        q.pop();
        stringstream ss;
        ss<<tmp;
        int x;
        ss>>x;
        lucky[lcnt++]=x;
        if(x>10005)continue;
        q.push(tmp+"4");
        q.push(tmp+"7");
    } 
}

vector<int> nei[100005];
int gro;
int siz[100005];
bool vis[100005];
void dfs(int v)
{
    if(vis[v]) return;
    vis[v]=1;
    siz[gro]++;
    for(int i=0;i<nei[v].size();i++)
    {
        int u=nei[v][i];
        dfs(u);
    }
}
int n,m;
int dp[500005];
void solve(int x,int y)
{
    for(int i=n;i>=x;i--)
    {
        if(dp[i-x]!=-1) 
        {
            if(dp[i]!=-1)dp[i]=min(dp[i-x]+y,dp[i]); else dp[i]=dp[i-x]+y;
        }
    }
}
int main()
{
    init();
    cin>>n>>m;
    for(int i=0;i<m;i++)
    {
        int x,y;
        cin>>x>>y;
        x--,y--;
        nei[x].push_back(y);
        nei[y].push_back(x);
    }
    map<int,int> num;
    for(int i=0;i<n;i++)
    {
        if(!vis[i])
        {
            dfs(i);
            num[siz[gro]]++;
            gro++;
        }
    }
    memset(dp,-1,sizeof dp);
    dp[0]=0;
    map<int,int>::iterator it=num.begin();
    for(;it!=num.end();it++)
    {
        int x=it->first,y=it->second;
        for(int j=1;j<y;j<<=1)
        {
            solve(j*x,j);
            y-=j;
        }
        solve(y*x,y);
    }
    int mi=1e9;
    for(int i=0;i<lcnt;i++)
    {
        if(dp[lucky[i]]!=-1)mi=min(dp[lucky[i]],mi);
    }
    if(mi!=1e9)cout<<mi-1; else cout<<-1;
    return 0;
}

还有一种优化的方法,可以使用完全背包问题的优化方法

 1 #include <bits/stdc++.h>
 2 #define INF 1000000009
 3 using namespace std;
 4 
 5 int n,m,dp[100010],num[100010];
 6 bool vis[100010];
 7 vector<int> v[100010],ans;
 8 
 9 int dfs(int u)
10 {
11     int sum=0;
12     for(int i=0;i<v[u].size();i++) if(!vis[v[u][i]]){
13         vis[v[u][i]]=true;
14         sum+=dfs(v[u][i])+1;
15     }
16     return sum;
17 }
18 
19 bool lucky(int x)
20 {
21     if(x==0) return false;
22     bool flag=true;
23     while(x!=0){
24         int y=x%10;
25         if(y!=4&&y!=7) flag=false;
26         x/=10;
27     }
28     return flag;
29 }
30 
31 int main()
32 {
33     cin>>n>>m;
34     for(int i=1;i<=m;i++){
35         int x,y;
36         cin>>x>>y;
37         v[x].push_back(y);
38         v[y].push_back(x);
39     }
40     for(int i=1;i<=n;i++) if(!vis[i]){
41         vis[i]=true;
42         int nm=dfs(i)+1;
43         if(num[nm]==0) ans.push_back(-nm),num[nm]=1;
44         else num[nm]++;
45     }
46     sort(ans.begin(),ans.end());
47     dp[0]=0;
48     for(int i=1;i<=n;i++) dp[i]=INF;
49     for(int i=0;i<ans.size();i++){
50         map<int,int> mp;
51         for(int j=-ans[i];j<=n;j++){
52             if(dp[j]>dp[j+ans[i]]+1&&mp[j+ans[i]]+1<=num[-ans[i]]){
53                 dp[j]=dp[j+ans[i]]+1;
54                 mp[j]=mp[j+ans[i]]+1;
55             }
56         }
57     }
58     int ans=INF;
59     for(int i=0;i<=n;i++){
60         if(lucky(i)) ans=min(ans,dp[i]);
61     }
62     if(ans==INF) ans=0;
63     cout<<ans-1<<endl;
64     return 0;
65 }

 

posted @ 2018-07-19 17:03  双零  阅读(155)  评论(0)    收藏  举报