题目链接~~>
做题感悟:这题以前看过但是没做出来,也不知道用状态压缩,今天做了一下竟然1A ,悲剧的是AC 之后我看别人都没排序,so~>自己也罢排序的删掉结果就wa了,这是为什么???
解题思路:状态压缩 + 记忆化搜索
dp[ S ] 代表达到状态 S 所减少的分数,其中S中的每一位代表某项作业到达状态 S 该作业已经做过。那么 dp[ S ] = min { dp [ S^(1<<i) ] + mx } ( 0 <= i < n ,mx 只加上i后减少的分数) ,还要开一个数组记录结束的天数,这里到达同一种状态所用的总天数是一样的。然后记忆化搜索就ok 了 。这里有一点不明白的是:问什么我不排序就wa,难道这靠人品吗??求指教??
代码:
#include<iostream> #include<sstream> #include<map> #include<cmath> #include<fstream> #include<vector> #include<sstream> #include<cstring> #include<cstdio> #include<stack> #include<queue> #include<bitset> #include<ctime> #include<string> #include<iomanip> #include<algorithm> using namespace std ; #define INT long long int const int INF = 0x3f3f3f3f ; const double esp = 0.00000001 ; const double PI = acos(-1.0) ; const int mod = 6 ; const int MY = 8 ; const int MX = 50000 + 5 ; int n ; int pre[MX] ; int dp[MX] ,day[MX] ,pt[MX] ; struct node { char s[105] ; int x ,y ; }T[MX] ; bool cmp(node a ,node b) { return strcmp(a.s ,b.s) > 0 ; } void input() { scanf("%d" ,&n) ; memset(dp ,-1 ,sizeof(dp)) ; dp[0] = day[0] = pre[0] = 0 ; for(int i = 0 ;i < n ; ++i) { scanf("%s%d%d" ,T[i].s ,&T[i].x ,&T[i].y) ; pre[1<<i] =1<<i ; } sort(T ,T+n ,cmp) ; // 排序 } int DP(int S) // 记忆化搜索 { if(dp[S] != -1) return dp[S] ; int& ans = dp[S] ; ans = day[S] = INF ; int temp ,d ,cost ,end ; for(int i = 0 ;i < n ; ++i) if((S&(1<<i))) { temp = DP(S^(1<<i)) ; d = day[S^(1<<i)] ; end = d + T[i].y ; if(T[i].x <= d) cost = d-T[i].x + T[i].y ; else { if(T[i].x-d >= T[i].y) cost = 0 ; else cost = T[i].y - T[i].x + d ; } if(ans > temp + cost) { ans = temp+cost ; day[S] = end ; pre[S] = S^(1<<i) ; } } return ans ; } void print(int S) // 递归输出 { if(pre[S] != S) { print(pre[S]) ; cout<<T[pt[pre[S]^S]].s<<endl ; } else return ; } void init() { for(int i = 0 ;i < 15 ; ++i) pt[1<<i] = i ; } int main() { init() ; int Tx ; scanf("%d" ,&Tx) ; while(Tx--) { input() ; cout<<DP((1<<n)-1)<<endl ; print((1<<n)-1) ; } return 0 ; }
HDU 1789 Doing Homework again
解题思路:这题意思和上面一题差不多,改变了一点就可以用贪心解决。
方法一、对截至时间排序,如果时间相等则价值从大到小排。
需要用一个数记录已经写了几天了,因为时间是排好序的,如果处理到第 i 个,如果第 i 个的截至时间小于前面的累计时间那么记录天数加一就可以。如果第 i 个的截至时间大于等于前面累计的时间,就在前面找一个价值最小的且没被标记的任务标记,等于替换了最小的价值的任务,这样最终的结果为标记了的任务的价值。
方法二、对价值排序,如果价值相等时间从小到达排。
价值从大到小排序,如果当前处理第 i 个任务,则从当前任务的截至时间开始往前找,看那一天没有被标记过,如果没有找到就应该加到最终的结果中,尽量选择靠近截止时间的那天做当前作业。
代码(方法一):
#include<iostream> #include<sstream> #include<map> #include<cmath> #include<fstream> #include<vector> #include<sstream> #include<cstring> #include<cstdio> #include<stack> #include<queue> #include<bitset> #include<ctime> #include<string> #include<iomanip> #include<algorithm> using namespace std ; #define INT long long int const int INF = 0x3f3f3f3f ; const double esp = 0.00000001 ; const double PI = acos(-1.0) ; const int mod = 6 ; const int MY = 8 ; const int MX = 1000 + 5 ; int n ; bool vis[MX] ; struct node { int x ,y ; }T[MX] ; bool cmp(node a ,node b) { if(a.x != b.x) return a.x < b.x ; else return a.y > b.y ; } void input() { scanf("%d" ,&n) ; for(int i = 1 ;i <= n ; ++i) scanf("%d" ,&T[i].x) ; for(int i = 1 ;i <= n ; ++i) scanf("%d" ,&T[i].y) ; sort(T+1 ,T+n+1 ,cmp) ; } void solve() { memset(vis ,false ,sizeof(vis)) ; int num = 0 ,ans = 0 ; for(int i = 1 ;i <= n ; ++i) { if(num < T[i].x) num++ ; else { int flag = i ,Max = T[i].y ; for(int j = 1 ;j < i ; ++j) if(!vis[j] && T[j].y < Max) // 如果没被标记 { Max = T[j].y ; flag = j ; } if(flag == i) { vis[i] = true ; ans += T[i].y ; } else { vis[flag] = true ; ans += Max ; } } } cout<<ans<<endl ; } int main() { int Tx ; scanf("%d" ,&Tx) ; while(Tx--) { input() ; solve() ; } return 0 ; }代码(方法二):
#include<iostream> #include<sstream> #include<map> #include<cmath> #include<fstream> #include<vector> #include<sstream> #include<cstring> #include<cstdio> #include<stack> #include<queue> #include<bitset> #include<ctime> #include<string> #include<iomanip> #include<algorithm> using namespace std ; #define INT long long int const int INF = 0x3f3f3f3f ; const double esp = 0.00000001 ; const double PI = acos(-1.0) ; const int mod = 6 ; const int MY = 8 ; const int MX = 1000 + 5 ; int n ; bool vis[MX] ; struct node { int x ,y ; }T[MX] ; bool cmp(node a ,node b) { if(a.y != b.y) return a.y > b.y ; else return a.x < b.x ; } void input() { scanf("%d" ,&n) ; for(int i = 1 ;i <= n ; ++i) scanf("%d" ,&T[i].x) ; for(int i = 1 ;i <= n ; ++i) scanf("%d" ,&T[i].y) ; sort(T+1 ,T+n+1 ,cmp) ; } void solve() { int ans = 0 ; memset(vis ,false ,sizeof(vis)) ; for(int i = 1 ;i <= n ; ++i) { int j = T[i].x ; for( ;j >= 1 ; --j) if(!vis[j]) { vis[j] = true ; break ; } if(j == 0) ans += T[i].y ; } cout<<ans<<endl ; } int main() { int Tx ; scanf("%d" ,&Tx) ; while(Tx--) { input() ; solve() ; } return 0 ; }