状态压缩+dfs+背包

状态压缩dp

状态压缩dp就是用二进制数字来表示状态,通过遍历所有状态确定答案,时间复杂度为o(nn2^n),通常只能用来解数据范围小于等于21的。
P1433 吃奶酪

#include <bits/stdc++.h>
#define eps 1e-15
using namespace std;
/*
       不要被困在一种思路中
*/
//dp i j 表示在i状态下位于j位跑动的最小距离
double dis[20][20],ans=0xffffffff,dp[35000][20],r[20],c[20];

double getdis(int i,int j){
    return sqrt((r[i]-r[j])*(r[i]-r[j])+(c[i]-c[j])*(c[i]-c[j]));
}

int main()
{
    int n;
    cin>>n;
    memset(dp,127,sizeof(dp));
    for(int i=1; i<=n; i++)cin>>r[i]>>c[i];

    for(int i=0; i<=n; i++)
        for(int j=i; j<=n; j++)
        dis[i][j]=getdis(i,j),dis[j][i]=dis[i][j];

    for(int i=1; i<=n; i++){
        dp[1<<(i-1)][i]=dis[0][i];
    }

    for(int i=1; i<=(1<<n)-1; i++){
        for(int j=1; j<=n; j++){
            if(i&(1<<(j-1))==0)continue;
            for(int k=1; k<=n; k++){
                if(j==k)continue;
                if(i&(1<<(k-1))==0)continue;
                dp[i][j]=min(dp[i][j],dp[i&~(1<<(j-1))][k]+dis[j][k]);
            }
        }
    }
    for(int i=1; i<=n; i++){
        ans=min(ans,dp[(1<<n)-1][i]);
    }
    printf("%.2lf",ans);
    return 0;
}

这个题还可以使用dfs+状态压缩优化来解,可以通过记录状态来做出一个记忆化搜索。当通过的位置确定了,无论是怎么走的,当前所走的最小步数也就确定了。

#include <bits/stdc++.h>
#define eps 1e-15
using namespace std;
double bestv=0xffffffff;
int n;
double dp[65000][20];
map<pair<double,double>,int> visit;
pair<double,double> a[20];

double getdis(pair<double,double> p1,pair<double,double> p2){
    return sqrt(pow(p1.first-p2.first,2)+pow(p1.second-p2.second,2));
}

void dfs(int i,double now,int bi,pair<double,double> p){
    if(i==n){
        bestv=min(bestv,now);
        return;
    }
    for(int j=1; j<=n; j++){
    //bt记录当前状态
        int bt=bi|(int)pow(2,j-1);
        if(visit.find(a[j])->second)continue;
        if(now+getdis(p,a[j])>=bestv)continue;
       	//剪枝
        if(now+getdis(p,a[j]) > dp[bt][j])continue;
        //更新状态
        dp[bt][j]=now+getdis(p,a[j]);
        visit.find(a[j])->second=1;
        dfs(i+1, now+getdis(p,a[j]),bt,a[j]);
        visit.find(a[j])->second=0;
    }
/*    for(int j=1; j<=n; j++){
        if(visit.find(a[j])->second)continue;
        if(now+getdis(p,a[j])>=bestv)continue;
        visit.find(a[j])->second=1;
        dfs(i+1, now+getdis(p,a[j]), a[j]);
        visit.find(a[j])->second=0;
    }*/
}

/*
       不要被困在一种思路中
*/

int main()
{
    //freopen("d://P1433_5.in","r",stdin);
    //freopen("d://out.txt","w",stdout);
    cin>>n;
    for(int i=1; i<=n; i++){
        cin>>a[i].first>>a[i].second;
        visit.insert(map<pair<double,double>,int>::value_type(a[i],0));
    }
    memset(dp,127,sizeof(dp));
    pair<double,double> t(0,0);
    dfs(0,0,0,t);
    printf("%.2lf",bestv);
    return 0;
}

Game of Ball Passing
看着挺简单的,就是不会。首先如果最大的数有两个及以上,那么一个球满足条件,根据此可以抽出最大的数maxx和剩余数的和sum,如果sum>=maxx,那么一定可以造出两个最大的数。如果小于那么肯定不满足,那么需要maxx-sum个球(此时一个球最多消耗maxx的sum+1)

#include <bits/stdc++.h>
#define eps 1e-15
using namespace std;
/*
       不要被困在一种思路中
*/
void solve(){
    long long n,a,maxx=-1,sum=0;
    cin>>n;
    for(int i=1; i<=n; i++){
        cin>>a;
        maxx=max(maxx,a);
        sum+=a;
    }
    sum-=maxx;
    if(sum >= maxx && sum!=0)cout<<1<<endl;
    else cout<<maxx-sum<<endl;
}
 
int main()
{
    int t;
    cin>>t;
	while(t--)
        solve();
    return 0;
}

Cow Frisbee Team S
0-1背包,因为数据量的问题,需要运用求余小技巧。感觉如果使用一重数组,不论j从小到大还是从大到小,都会变成完全背包,归根结底还是对j取模的原因

#include <bits/stdc++.h>
#define eps 1e-15
#define mod 100000000
using namespace std;
/*
       不要被困在一种思路中
*/
//dp i j表示选第i头牛,队伍能力为对j取余,的方案数量
long long n,f,a[2005],dp[2005][1005];

int main()
{
    //freopen("d://P2946_2.in","r",stdin);
    cin>>n>>f;
    for(int i=1; i<=n; i++)cin>>a[i];
    dp[0][f]=1;
    for(int i=1; i<=n; i++){
        for(int j=0; j<=f; j++){
            dp[i][j]+=dp[i-1][j];
            dp[i][(j+a[i])%f]=(dp[i][(j+a[i])%f]%mod+dp[i-1][j]%mod)%mod;
        }
    }
	cout<<dp[n][0]%mod;
    return 0;
}

质数和分解
完全背包

**#include <bits/stdc++.h>
#define eps 1e-15
using namespace std;
/*
       不要被困在一种思路中
*/
//dp j 表示选完第i个数,剩余数为j的方案数
int n,dp[205];

bool judge(int x){
    if(x==1)return false;
    bool f=1;
    for(int i=2; i<=x/2; i++)
        if(x%i==0){f=0;break;}
    return f;
}

void solve(){
    dp[n]=1;
    for(int i=2; i<=n; i++){
        if(!judge(i))continue;
        for(int j=n; j>=0; j--){
        if(j+i>n)continue;
        dp[j]+=dp[j+i];
        }
    }
}

int main()
{
    while(scanf("%d",&n)!=EOF){
        solve();
        cout<<dp[0]<<endl;
        memset(dp,0,sizeof(dp));
    }

    return 0;
}**

Buying Hay S
完全背包

#include <bits/stdc++.h>
#define eps 1e-15
using namespace std;
/*
       不要被困在一种思路中
*/
//dp j表示j磅花费的价钱
int n,h,dp[55005],ans=0xfffffff;
pair<int, int> a[105];

int main()
{
    cin>>n>>h;
    for(int i=1; i<=n; i++)cin>>a[i].first>>a[i].second;
    memset(dp,127,sizeof(dp));
    dp[0]=0;
    for(int i=1; i<=n; i++){
    for(int j=0; j<=h; j++){
        dp[j+a[i].first]=min(dp[j+a[i].first], dp[j]+a[i].second);
    }
/*    for(int i=0; i<=h; i++)
        cout<<dp[i]<<" ";
    cout<<endl;*/
    }
    for(int i=h; i<=55004; i++)
        ans=min(ans,dp[i]);
    cout<<ans;
    return 0;
}

River Crossing S
0-1背包

#include <bits/stdc++.h>
#define eps 1e-15
using namespace std;
/*
       不要被困在一种思路中
*/
//dp i j表示运完第i头牛后,船上剩下的牛的数量
int dp[2505][2505],a[2505],n,m;
void solve(){
    int minn;
    dp[1][0]=m+a[1]+m;dp[1][1]=0;
    for(int i=2; i<=n; i++){
        dp[i][0]=dp[i-1][0]+m+a[1]+m;
        for(int j=1; j<=i-1; j++){
        //只需要计算每次运完后剩下0头牛的最小值,分为先运j头牛再运第i头牛和一起运j+1头牛
            dp[i][0]=min(min(dp[i-1][j]+a[j]+2*m+a[1]+2*m,dp[i-1][j]+a[j+1]+2*m),dp[i][0]);
        }
        for(int j=1; j<=i; j++)
            dp[i][j]=dp[i-1][j-1];
    }
    minn=dp[n][0]-m;
    for(int i=1; i<=n; i++)
        minn=min(dp[n][i]+a[i]+m,minn);
    cout<<minn;
}

int main()
{
    cin>>n>>m;
    for(int i=1; i<=n; i++){
        cin>>a[i];
        a[i]+=a[i-1];
    }
    solve();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值