状态压缩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;
}