HDU 1074 Doing Homework ( 状态压缩 )

来源:互联网 发布:乐至县广电网络客服 编辑:程序博客网 时间:2024/05/14 10:54

Doing Homework

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 10297    Accepted Submission(s): 4963


Problem Description
Ignatius has just come back school from the 30th ACM/ICPC. Now he has a lot of homework to do. Every teacher gives him a deadline of handing in the homework. If Ignatius hands in the homework after the deadline, the teacher will reduce his score of the final test, 1 day for 1 point. And as you know, doing homework always takes a long time. So Ignatius wants you to help him to arrange the order of doing homework to minimize the reduced score.
 

Input
The input contains several test cases. The first line of the input is a single integer T which is the number of test cases. T test cases follow.
Each test case start with a positive integer N(1<=N<=15) which indicate the number of homework. Then N lines follow. Each line contains a string S(the subject's name, each string will at most has 100 characters) and two integers D(the deadline of the subject), C(how many days will it take Ignatius to finish this subject's homework). 

Note: All the subject names are given in the alphabet increasing order. So you may process the problem much easier.
 

Output
For each test case, you should output the smallest total reduced score, then give out the order of the subjects, one subject in a line. If there are more than one orders, you should output the alphabet smallest one.
 

Sample Input
23Computer 3 3English 20 1Math 3 23Computer 3 3English 6 3Math 6 3
 

Sample Output
2ComputerMathEnglish3ComputerEnglishMath
Hint
In the second test case, both Computer->English->Math and Computer->Math->English leads to reduce 3 points, but the word "English" appears earlier than the word "Math", so we choose the first order. That is so-called alphabet order.

123


 【状态压缩】


正好学习一下 状态压缩;

状压 用的 最多的是 &  |  << >>  运算;  对于 n <  一般小于20 (2^20 已经不小了  一般15,10)的 范围内;   我们把某个状态 用 0/1 来表示,   这样就有 2^n 个状态

然后枚举状态; 在当前状态下, 判断某个情况是否是符合  符合的话进行下一步操作;


四种运算:  (搬过来的)

1.’&’符号,x&y,会将两个十进制数在二进制下进行与运算,然后返回其十进制下的值。例如3(11)&2(10)=2(10)。

2.’|’符号,x|y,会将两个十进制数在二进制下进行或运算,然后返回其十进制下的值。例如3(11)|2(10)=3(11)。

3.’^’符号,x^y,会将两个十进制数在二进制下进行异或运算,然后返回其十进制下的值。例如3(11)^2(10)=1(01)。

4.’<<’符号,左移操作,x<<2,将x在二进制下的每一位向左移动两位,最右边用0填充,x<<2相当于让x乘以4。相应的,’>>’是右移操作,x>>1相当于给x/2,去掉x二进制下的最有一位。

以及应用;

1.判断一个数字x二进制下第i位是不是等于1。

方法:if ( ( ( 1 << ( i - 1 ) ) & x ) > 0)

将1左移i-1位,相当于制造了一个只有第i位上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果>0,说明x第i位上是1,反之则是0。

2.将一个数字x二进制下第i位更改成1。

方法:x = x | ( 1<<(i-1) )

证明方法与1类似,此处不再重复证明。

3.把一个数字二进制下最靠右的第一个1去掉。

方法:x=x&(x-1)



应用用起来最多;


【HDU 1074】

按字典序,  那么就要记录 路径,   这里用stack   pre记录前驱  记录;

【代码】

//#include <bits/stdc++.h>#include <iostream>#include <stdio.h>#include <algorithm>#include <cmath>#include <math.h>#include <cstring>#include <string>#include <queue>#include <stack>#include <stdlib.h>#include <list>#include <map>#include <set>#include <bitset>#include <vector>#define mem(a,b) memset(a,b,sizeof(a))#define findx(x) lower_bound(b+1,b+1+bn,x)-b#define FIN      freopen("input.txt","r",stdin)#define FOUT     freopen("output.txt","w",stdout)#define S1(n)    scanf("%d",&n)#define SL1(n)   scanf("%I64d",&n)#define S2(n,m)  scanf("%d%d",&n,&m)#define SL2(n,m)  scanf("%I64d%I64d",&n,&m)#define Pr(n)     printf("%d\n",n)#define lson rt << 1, l, mid#define rson rt << 1|1, mid + 1, r#define mem(a,b) memset(a,b,sizeof(a))typedef long long ll;const int INF=0x3f3f3f3f;const ll MOD=1e9+7;const int MAXN=1e5+5;const int N=16;ll qpow(ll x,ll n){ll res=1;for(;n;n>>=1){if(n&1)res=(res*x);x=(x*x);}return res;}using namespace std;struct node{    string name;    int cost,dead;}a[100];struct nnode{    int time,pre,score,now;}dp[1<<N];int main(){    int T;    cin>>T;    int n;    while(T--)    {        scanf("%d",&n);        for(int i=0;i<n;i++)        {            cin>>a[i].name>>a[i].dead>>a[i].cost;        }        mem(dp,0);        int bit= 1<<n;        for(int i=1;i<bit;i++)  // 枚举状态        {            dp[i].score=INF;            for(int j= n-1;j>=0;j--) // 枚举 全部作业   题意,  做完某一个 就放在后面, 那么看状态, j前面的是否能够到达j            {                int te= 1<<j;                if( te & i)                {                    int past = i -te  ;                    int val = dp[past].time + a[j].cost-a[j].dead;                    if(val<0) val=0;                    if(val + dp[past].score < dp[i].score)                    {                        dp[i].score= val + dp[past].score;                        dp[i].now=j;                        dp[i].pre= past;                        dp[i].time= dp[past].time + a[j].cost;                    }                }            }        }        stack<int>Q;        int te= bit-1;        printf("%d\n",dp[te].score);        while(te)        {            Q.push(dp[te].now);            te= dp[te].pre;        }        while(!Q.empty())        {            cout<<a[Q.top()].name<<endl;            Q.pop();        }    }    return 0;}


原创粉丝点击