1. 程式人生 > >【常規的01揹包 POJ3624 UVA562 HDU2546 HDU3466 poj1745】

【常規的01揹包 POJ3624 UVA562 HDU2546 HDU3466 poj1745】

POJ3624
有N個物品,分別有不同的重量Wi和價值Di,Bessie只能帶走重量不超過M的物品,要是總價值最大,並輸出總價值

//#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
using namespace std;
int dp[maxn];
int c[maxn];
int w[maxn];
int main()
{
  int n,m;
  while(cin>>n>>m)
  {
        for(int i=0; i<n; ++i){cin>>c[i]>>w[i];}

        for(int i=0; i<n; ++i)
            for(int j=m; j>=c[i]; --j)
                dp[j]=max(dp[j],dp[j-c[i]]+w[i]);
        cout<<dp[m]<<endl;
  }
    return 0;
}

UVA562
給定n個硬幣,要求將這些硬幣平分以使兩個人獲得的錢儘量多,求兩個人分到的錢最小差值。
 

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
const ll moad=1e9+7;
const int maxn=1e6+10;
int dp[maxn];
int w[maxn];
int vis[maxn];
int main()
{
    int t,n;
    cin>>t;
    while(t--)
    {
        cin>>n;
        int sum=0;
        for(int i=0;i<n;++i)cin>>w[i],sum+=w[i];
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;++i)
        {
            for(int j=sum/2;j>=w[i];--j)
            {
                dp[j]=max(dp[j],dp[j-w[i]]+w[i]);
            }
        }
        int __=dp[sum/2],_;
            _=sum-__;
        cout<<abs(__-_)<<endl;
    }
    return 0;
}

HDU2546
這題看上去就知道是01問題了,可是開始怎麼轉化都沒有轉化為正確的01揹包姿勢
因為沒有明顯的給出來一個揹包的容量(做完之後,看出來其實是給出來的,就是這個m-5),然後我wa的姿勢是找所有物品的價值總和去作為容量
撒了一眼題解看是m-5,才知道可以用這個-5剩下的錢去01揹包買更多的東西,最後加上那個5塊錢去買加個最大的物品
這個5塊好像用到了貪心的思想 mmp
總結:轉化為正確的01問題姿勢是關鍵
 

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e6+10;
int dp[maxn];
int w[1005];
int main()
{
    int n,m;
    while(cin>>n,n)
    {
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;++i) cin>>w[i];
        cin>>m;
        if(m<5){cout<<m<<endl;continue;}
        sort(w,w+n);
        int _now=m-5;
        for(int i=0;i<n-1;++i)
        {
            for(int j=_now;j>=w[i];--j)
                dp[j]=max(dp[j],dp[j-w[i]]+w[i]);
        }
        cout<<m-(w[n-1]+dp[_now])<<endl;
    }
    return 0;
}
#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e6+10;
int dp[maxn];
int w[1005];
int main()
{
    int n,m;
    while(cin>>n,n)
    {
        int ans=0;
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;++i) cin>>w[i],ans+=w[i];
        cin>>m;
        if(m<5){cout<<m<<endl;continue;}

        for(int i=0;i<n;++i)
        {
            for(int j=ans;j>=w[i];--j)
                dp[j]=max(dp[j],dp[j-w[i]]+w[i]);
        }
        cout<<m-dp[ans]<<endl;
    }
    return 0;
}

HDU3466
先前得01揹包問題都是佔用多大空間,有多大空間就可以直接進行放入,而這個有個限制體條件
當容量低於特定值得時候就不可以放入,那這樣進行放入得原則就是 需要的容量/本身佔有得容量大小  這個值越小越好,所謂得價效比越高越好嘛
還有要注意的一點就是當 揹包容量小於給的特定值得時候就不可以進行更新dp陣列了

#include <bits/stdc++.h>
#include <iostream>
#define X 10005
#define inf 0x3f3f3f3f
#define PI 3.141592653589793238462643383
#define IO  ios::sync_with_stdio(false),cin.tie(0), cout.tie(0);
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
typedef long long ll;
const ll moad=1e9+7;
const int maxn=1e6+10;
int q[maxn];
int c[maxn];
int w[maxn];
int dp[maxn];
struct node
{
    double c,q,w;
}point[maxn];
bool cmp(node a,node b)
{
    //return a.q-a.c<b.q-b.c;
    //return a.c*b.q<b.c*a.q;
    return a.q/a.c<=b.q/b.c;
}
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;++i)cin>>point[i].c>>point[i].q>>point[i].w;
           sort(point,point+n,cmp);
//           for(int i=0;i<n;++i)
//            cout<<point[i].c<<' '<<point[i].q<<' '<<point[i].w<<endl;
            for(int i=0;i<n;++i)
            {
                for(int j=m;j>=point[i].q;--j)
                {
                    dp[j]=max(dp[j],dp[j-int(point[i].c)]+int(point[i].w));
                }
            }
        cout<<dp[m]<<endl;
    }
    return 0;
}

poj1745

給你n個數,經過加減的操作,看能不能整除K。
dp[i][j]代表前i個數字整除 m 的餘數為 j
 

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#define X 10005
using namespace std;
int main()
{
    int N,K;
    cin>>N>>K;
    int a[X];
    for(int i=1;i<=N;++i)
    {
        scanf("%d",&a[i]);
        a[i]%=K;
    }
    int dp[X][K];
    memset(dp,0,sizeof(dp));
    //if(a[1]<0)
    //    a[1]+=K;
  a[1]=(a[1]+K)%K;
  dp[1][a[1]]=1;
    for(int i=2;i<=N;++i)
    {
        for(int j=0;j<K;++j)
        {
            if(dp[i-1][j])
            {
              /*  if(j+a[i]>=0)
                    dp[i][j+a[i]]=1;
                else
                    dp[i][j+a[i]+K]=1;
                if(j>=a[i])
                    dp[i][j-a[i]]=1;
                else
                    dp[i][j-a[i]+K]=1;*/
            dp[i][(j+a[i]+K)%K]=1;//保證下標大於0
            dp[i][(j-a[i]+K)%K]=1;
            }
        }
    }
    if(dp[N][0])
        cout<<"Divisible"<<endl;
    else
        cout<<"Not divisible"<<endl;
    return 0;
}

 

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <cstring>
#define X 10005
using namespace std;
long long POW(int a,int b)
{
    long long ans=1;
   while(b){
    if(b&1)
    {
        ans*=a;
    }
        a*=a;
        b>>=1;
   }
   return ans;
}
int main()
{
    int N,K;
    cin>>N>>K;
    int a[X];
    for(int i=0;i<N;++i)
    {
        scanf("%d",&a[i]);
    }
    long long n=POW(2,N-1);
    for(int i=0;i<n;++i)
    {
        int sum=a[0];//cout<<a[0];
        for(int j=0;j<N-1;++j)
        {
            if((i>>j)&1)
            {//cout<<'-'<<a[j+1];
                sum+=a[j+1];
            }
            else
                sum-=a[j+1];//,cout<<'+'<<a[j+1];
        }
        //cout<<'='<<sum<<"*****"<<sum%K<<endl;
       if(!(sum%K))//!的優先順序比%的高
        {
            cout<<"Divisible"<<endl;
            return 0;
        }
        //cout<<endl;
    }
    cout<<"Not divisible"<<endl;
    return 0;
}
//這個方法是對的,可是在這兒N是10000算出來的n是2^10000會炸的