CSU-ACM2018暑假集训比赛5 题解

POJ1157 LITTLE SHOP OF FLOWERS(dp)

Description

You want to arrange the window of your flower shop in a most pleasant way. You have F bunches of flowers, each being of a different kind, and at least as many vases ordered in a row. The vases are glued onto the shelf and are numbered consecutively 1 through V, where V is the number of vases, from left to right so that the vase 1 is the leftmost, and the vase V is the rightmost vase. The bunches are moveable and are uniquely identified by integers between 1 and F. These id-numbers have a significance: They determine the required order of appearance of the flower bunches in the row of vases so that the bunch i must be in a vase to the left of the vase containing bunch j whenever i < j. Suppose, for example, you have bunch of azaleas (id-number=1), a bunch of begonias (id-number=2) and a bunch of carnations (id-number=3). Now, all the bunches must be put into the vases keeping their id-numbers in order. The bunch of azaleas must be in a vase to the left of begonias, and the bunch of begonias must be in a vase to the left of carnations. If there are more vases than bunches of flowers then the excess will be left empty. A vase can hold only one bunch of flowers.  Each vase has a distinct characteristic (just like flowers do). Hence, putting a bunch of flowers in a vase results in a certain aesthetic value, expressed by an integer. The aesthetic values are presented in a table as shown below. Leaving a vase empty has an aesthetic value of 0.

According to the table, azaleas, for example, would look great in vase 2, but they would look awful in vase 4.  To achieve the most pleasant effect you have to maximize the sum of aesthetic values for the arrangement while keeping the required ordering of the flowers. If more than one arrangement has the maximal sum value, any one of them will be acceptable. You have to produce exactly one arrangement.

Input

  • The first line contains two numbers: F, V.
  • The following F lines: Each of these lines contains V integers, so that Aij is given as the jth number on the (i+1)st line of the input file.

  • 1 <= F <= 100 where F is the number of the bunches of flowers. The bunches are numbered 1 through F.

  • F <= V <= 100 where V is the number of vases.

  • -50 <= Aij <= 50 where Aij is the aesthetic value obtained by putting the flower bunch i into the vase j.

Output

The first line will contain the sum of aesthetic values for your arrangement.

Sample Input

3 5
7 23 -5 -24 16
5 21 -4 10 23
-21 5 -4 -20 20

Sample Output

53

题意

题目是给出F个花束和V个花瓶,其中花束按从左到右的顺序插入花瓶。必须所有花都能插入花瓶。对于每束花,插入花瓶的美观度不同,求美观度的最小值。

思路

对于该问题,实际上我们入手考虑的时候意识到了这是一个按照顺序,选择所属状态的情况。而按照顺序组成的状态数非常多,但实际上我们可以每次更新对于第i束花,将其放置在第j个花瓶中所得到的美观度的最大值。然后可以看出这个是对于第i+1束花的最优子结构,于是我们得到状态转移的方程为:dp[i][j] = max(dp[i][j-1],dp[i-1][j-1]+A[i][j]);当然还是要注意F和V相等的情况,以及所有值为负值的情况。所以初始值应当置作-INF。

代码

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int INF = 0x3f3f3f3f;

#define REP(i,n) for(int i=0;i<(n);i++)

const int MAXN= 1e2+10;
int A[MAXN][MAXN];
int F,V;
int dp[MAXN][MAXN];

void init(){
    memset(A,0,sizeof(A));
    memset(dp,-INF,sizeof(dp));
}

int main(){
    while(~scanf("%d %d",&F,&V)){
        init();
        for(int i=1;i<=F;i++){
            for(int j=1;j<=V;j++){
                scanf("%d",&A[i][j]);
            }
        }
        if(F==V){
            for(int i=1;i<=F;i++){
                dp[i][i]=dp[i-1][i-1]+A[i][i];
            }
        }else{
            for(int i=0;i<=F;i++){
                for(int j=i;j<=V;j++){
                    dp[i][j] = max(dp[i][j-1],dp[i-1][j-1]+A[i][j]);
                }
            }
        }
        printf("%d\n",dp[F][V]);
    }
    return 0;
}

Codeforces 999D Equalize the Remainders(思维)

You are given an array consisting of nn integers a1,a2,…,ana1,a2,…,an, and a positive integer mm. It is guaranteed that mm is a divisor of nn.

In a single move, you can choose any position ii between 11 and nn and increase aiai by 11.

Let's calculate crcr (0≤r≤m−1)0≤r≤m−1) — the number of elements having remainder rr when divided by mm. In other words, for each remainder, let's find the number of corresponding elements in aa with that remainder.

Your task is to change the array in such a way that c0=c1=⋯=cm−1=nmc0=c1=⋯=cm−1=nm.

Find the minimum number of moves to satisfy the above requirement.

Input

The first line of input contains two integers nn and mm (1≤n≤2⋅105,1≤m≤n1≤n≤2⋅105,1≤m≤n). It is guaranteed that mm is a divisor of nn.

The second line of input contains nn integers a1,a2,…,ana1,a2,…,an (0≤ai≤1090≤ai≤109), the elements of the array.

Output

In the first line, print a single integer — the minimum number of moves required to satisfy the following condition: for each remainder from 00to m−1m−1, the number of elements of the array having this remainder equals nmnm.

In the second line, print any array satisfying the condition and can be obtained from the given array with the minimum number of moves. The values of the elements of the resulting array must not exceed 10181018.

Examples

input

6 3
3 2 0 6 10 12

output

3
3 2 0 7 10 14 

input

4 2
0 1 2 3

output

0
0 1 2 3 

## 题意

给出数N,M,每次操作可以使任意值加1,求出使得N%M的M个值的个数相等的最小操作数。

思路

建立一个set存储0-M-1并维护,建立一个数组保存已经处理完成的值。当数目达到N/M时,set擦除该值。查找大于等于该值的值,否则将该值进行操作直到N%M为0,然后在进行查找。

代码

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int INF = 0x3f3f3f3f;

#define REP(i,n) for(int i=0;i<(n);i++)

const int MAXN = 2e6+10;
ll a[MAXN];
set<ll>s1;
ll cnt[MAXN];
ll now;
ll ans;
ll n,m;


void init(){
    s1.clear();
    memset(a,0,sizeof(a));
    memset(cnt,0,sizeof(cnt));
}

int main(){
    while(~scanf("%d %d",&n,&m)){
        init();
        for(ll i=0;i<n;i++){
            scanf("%lld",&a[i]);
        }
        for(ll i=0;i<m;i++){
            s1.insert(i);
        }
        for(ll i=0;i<n;i++){
            ll res = a[i]%m;
            if(s1.lower_bound(res)!=s1.end()){
                now = *s1.lower_bound(res);
                ans+=now-res;
                a[i]+=now-res;
            }else{
                ans+=m-res;
                a[i]+=m-res;
                now = *s1.lower_bound(0);
                ans+=now;
                a[i]+=now;
            }
            cnt[now]++;
            if (cnt[now]==n/m){
                s1.erase(s1.find(now));
            }
        }
        printf("%lld\n",ans);
        for(ll i=0;i<n;i++){
            printf("%lld ",a[i]);
        }
        printf("\n");
    }
    return 0;
}

UVA1235 Anti Brute Force Lock(最小生成树)

Lately, there is one serious problem with Panda Land Safe Box: several safes have been robbed! The safes are using old 4-digits rolling lock combination (you only have to roll the digit, either up or down, until all four of them match the key). Each digit is designed to roll from 0 to 9. Rolling-up at 9 will make the digit become 0, and rolling-down at 0 will make the digit become 9. Since there are only 10000 possible keys, 0000 through 9999, anyone can try all the combinations until the safe is unlocked. What’s done is done. But in order to slow down future robbers’ attack, Panda Security Agency (PSA) has devised a new safer lock with multiple keys. Instead of using only one key combination as the key, the lock now can have up to N keys which has to be all unlocked before the safe can be opened. These locks works as the following:

• Initially the digits are at 0000.
• Keys can be unlocked in any order, by setting the digits in the lock to match the desired key and then pressing the UNLOCK button.
• A magic JUMP button can turn the digits into any of the unlocked keys without doing any rolling.
• The safe will be unlocked if and only if all the keys are unlocked in a minimum total amount of rolling, excluding JUMP (yes, this feature is the coolest one).
• If the number of rolling is exceeded, then the digits will be reset to 0000 and all the keys will be locked again. In other word, the state of the lock will be reset the cracking is failed.

PSA is quite confident that this new system will slow down the cracking, giving them enough time to identify and catch the robbers. In order to determine the minimum number of rolling needed, PSA wants you to write a program. Given all the keys, calculate the minimum number of rolls needed to unlock the safe.

Input

The first line of input contains an integer T, the number of test cases follow. Each case begins with an integer N (1 ≤ N ≤ 500), the number of keys. The next N lines, each contains exactly four digits number (leading zero allowed) representing the keys to be unlocked.

Output

For each case, print in a single line the minimum number of rolls needed to unlock all the keys.

Explanation for the 2nd case:

• Turn 0000 into 1111, rolls: 4
• Turn 1111 into 1155, rolls: 8
• Jump 1155 into 1111, we can do this because 1111 has been unlocked before.
• Turn 1111 into 5511 rolls: 8
Total rolls = 4 + 8 + 8 = 20

Sample Input

4
2 1155 2211
3 1111 1155 5511
3 1234 5678 9090
4 2145 0213 9113 8113

Sample Output

16
20
26
17

题意

按顺序转动到所有的密码即可开锁。其中,经过的状态可以直接跳回而不需要额外操作。

思路

非常明确的找最小生成树的题目。既然可以跳回到任意已经过的状态,所以,也就是寻找最小生成树即可。注意一些细节问题,主要有两部分。其一是转动时最小步骤,可能是0-9转动或者9-0转动。
其二是0000为初始状态,只能经过不能跳回。所以最开始应该首先不考虑0000,对剩余N个做最小生成树,然后枚举0000到N个的最小距离,其和便是最小值。

代码

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int INF = 0x3f3f3f3f;

#define REP(i,n) for(int i=0;i<(n);i++)
 
const int MAXN=5e4+10;
const int MAXM=3e6+10;
int fat[MAXN];
 
struct Edge{
    int u,v,w;
}edge[MAXM];

int tol;

void addedge(int u,int v,int w){
    edge[tol].u=u;
    edge[tol].v=v;
    edge[tol++].w=w;
}
 
bool cmp(Edge a,Edge b){
    return a.w<b.w;
}
 
int find(int x){
    if(fat[x]==x)   return x;
    else    return fat[x]=find(fat[x]);
}

int Kruskal(int n){
    sort(edge,edge+tol,cmp);
    int cnt=0;
    int ans=0;
    for(int i=0;i<tol;i++){
        int u=edge[i].u;
        int v=edge[i].v;
        int w=edge[i].w;
        int fu=find(u);
        int fv=find(v);
        if(fu!=fv){
            ans+=w;
            fat[fu]=fv;
            cnt++;
        }
        if(cnt==n-1)
            break;
    }
    return ans;
}

int poi[MAXN];

void init(){
    tol = 0;
    memset(poi,0,sizeof(poi));
    memset(edge,0,sizeof(edge));
    for(int i=0;i<MAXN;i++){
        fat[i] = i;
    }
}
int work(int a,int b){
    int ten = 1000;
    int t=0;
    for(int i=0;i<4;i++){
        int cura = a/ten;
        int curb = b/ten;
        t+=min((cura-curb+10)%10,(curb-cura+10)%10);
        a = a-ten*cura;
        b = b-ten*curb;
        ten/=10;
    }
    return t;
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        int N = 0;
        scanf("%d",&N);
        for(int i=0;i<N;i++){
            scanf("%d",&poi[i]);
        }
        for(int i=0;i<N;i++){
            for(int j=i+1;j<N;j++){
                int x = work(poi[i],poi[j]);
                addedge(i,j,x);
                addedge(j,i,x);
            }
        }
        int mmin = INF;
        for(int i=0;i<N;i++){
            mmin = min(work(0,poi[i]),mmin);
        }
        printf("%d\n",Kruskal(N)+mmin);
    }
}
 

Gym 101550A Artwork(并查集)

A template for an artwork is a white grid of n × m squares. The artwork will be created by painting q horizontal and vertical black strokes. A stroke starts from square (x1, y1), ends at square (x2, y2) (x1 = x2 or y1 = y2) and changes the color of all squares (x, y) to black where x1 ≤ x ≤ x2 and y1 ≤ y ≤ y2. The beauty of an artwork is the number of regions in the grid. Each region consists of one or more white squares that are connected to each other using a path of white squares in the grid, walking horizontally or vertically but not diagonally. The initial beauty of the artwork is 1. Your task is to calculate the beauty after each new stroke. Figure A.1 illustrates how the beauty of the artwork varies in Sample Input 1. 1 region 3 regions 3 regions 4 regions 3 regions

Input

The first line of input contains three integers n, m and q (1 ≤ n, m ≤ 1000, 1 ≤ q ≤ 104 ). Then follow q lines that describe the strokes. Each line consists of four integers x1, y1, x2 and y2 (1 ≤ x1 ≤ x2 ≤ n, 1 ≤ y1 ≤ y2 ≤ m). Either x1 = x2 or y1 = y2 (or both).

Output

For each of the q strokes, output a line containing the beauty of the artwork after the stroke.

Sample Input 1

4 6 5
2 2 2 6
1 3 4 3
2 5 3 5
4 6 4 6
1 6 4 6

Sample Output 1

1
3
3
4
3

题意

每次给出x1,x2,y1,y2表示该范围内的块数被黑色覆盖。输出此时白色的联通块数目。

思路

维护联通块数目,可以想到,每次造成影响后,我们只用关心4周和当前块的影响。因此对于是否属于同一类的问题,想到使用并查集进行处理的方式。当然,考虑本题之后,我们发现由后至前进行统计的方法更易处理。本题细节如下:其一,可能多次覆盖的块数有重叠,从后向前统计的时候,要有int数组保存情况,不能采用bool数组。其二,输出结果时,最开始的统计情况是不用输出的,即初始情况联通块数目为1。

代码

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int INF = 0x3f3f3f3f;

#define REP(i,n) for(int i=0;i<(n);i++)

const int MAXN = 1e3+10;
int vis[MAXN][MAXN];
int gra[MAXN][MAXN];
int dic[4][2] ={0,1,0,-1,-1,0,1,0};

struct query{
    int x1,x2,y1,y2;
}Q[MAXN*10];
int n,m,q;
int fat[MAXN*MAXN];

void init(){
    for(int i=0;i<MAXN*MAXN;i++){
        fat[i] = i;
    }
    memset(vis,0,sizeof(vis));
    memset(gra,0,sizeof(gra));
}

int find(int x){
    if(x==fat[x])   return x;
    else{
        int t = fat[x];
        fat[x] = find(t);
    }
    return fat[x];
}

int merge(int x,int y){
    int fx = find(x);
    int fy = find(y);
    if(fx==fy)  return 0;
    else{
        fat[fx] = fy;
    }   
    return 1;
}

int makeid(int x,int y){
    return (x-1)*m+y-1;
}

int check(int i,int j){
    if(i>0&&i<=n&&j>0&&j<=m)    return 1;
    else    return 0;
}

int caltans(int x,int y){
    int ans = 0;
    for(int i=0;i<4;i++){
        int curx = x+dic[i][0];
        int cury = y+dic[i][1];
        if(check(curx,cury)&&gra[curx][cury]==0){
            if(merge(makeid(curx,cury),makeid(x,y))){
                ans++;
            }
        }
    }
    return ans;
}

void dfs(int x,int y){
    vis[x][y]=1;
    for(int i=0;i<4;i++){
        int curx = x+dic[i][0];
        int cury = y+dic[i][1];
        if(check(curx,cury)&&vis[curx][cury]==0&&gra[curx][cury]==0){
            merge(makeid(x,y),makeid(curx,cury));
            dfs(curx,cury);
        }
    }
}

int count(){
    int ans = 0;
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m; j++) {
            if(vis[i][j]==0 && gra[i][j]==0) {
                ans++;
                dfs(i, j);
            }
        }
    }
    return ans;
}

int main(){
    while(~scanf("%d %d %d",&n,&m,&q)){
        init();
        for(int i=0;i<q;i++){
            scanf("%d %d %d %d",&Q[i].x1,&Q[i].y1,&Q[i].x2,&Q[i].y2);
            for(int j=Q[i].x1;j<=Q[i].x2;j++) {
                for(int k=Q[i].y1;k<=Q[i].y2;k++) {
                    gra[j][k]++;
                }
            }
        }
        int tans  = 0;
        stack<int> stk;
        tans = count();
        stk.push(tans);
        for(int i=q-1;i>=0;i--){
            for(int j=Q[i].x1;j<=Q[i].x2;j++){
                for(int k=Q[i].y1;k<=Q[i].y2;k++){
                    gra[j][k]--;
                    if(gra[j][k]==0){
                        tans++;
                        tans-= caltans(j,k);
                    }
                }
            }
            stk.push(tans);
        }
        stk.pop();
        while(stk.empty()==0 ){
            printf("%d\n", stk.top());
            stk.pop();
        }   
    }
    
    return 0;
}

ZOJ2270 Burn the Linked Camp(差分约束)

It is well known that, in the period of The Three Empires, Liu Bei, the emperor of the Shu Empire, was defeated by Lu Xun, a general of the Wu Empire. The defeat was due to Liu Bei's wrong decision that he divided his large troops into a number of camps, each of which had a group of armies, and located them in a line. This was the so-called "Linked Camps".

Let's go back to that time. Lu Xun had sent many scouts to obtain the information about his enemy. From his scouts, he knew that Liu Bei had divided his troops into n camps, all of which located in a line, labeled by 1..n from left to right. The ith camp had a maximum capacity of Ci soldiers. Furthermore, by observing the activities Liu Bei's troops had been doing those days, Lu Xun could estimate the least total number of soldiers that were lived in from the ith to the jth camp. Finally, Lu Xun must estimate at least how many soldiers did Liu Bei had, so that he could decide how many troops he should send to burn Liu Bei's Linked Camps.

Input

There are multiple test cases! On the first line of each test case, there are two integers n (0<n<=1,000) and m (0<=m<=10,000). On the second line, there are n integers C1��Cn. Then m lines follow, each line has three integers i, j, k (0<i<=j<=n, 0<=k<2^31), meaning that the total number of soldiers from the ith camp to the jth camp is at least k.

Output

For each test case, output one integer in a single line: the least number of all soldiers in Liu Bei's army from Lu Xun's observation. However, Lu Xun's estimations given in the input data may be very unprecise. If his estimations cannot be true, output "Bad Estimations" in a single line instead.

Sample Input

3 2
1000 2000 1000
1 2 1100
2 3 1300
3 1
100 200 300
2 3 600

Sample Output

1300
Bad Estimations

题意

给出n个点,m个区间。

n个点的值不能超过给出值。m个区间要满足,区间内的数目大于等于所给出的数目。

思路

差分约束的模板题目。设置点值是交换的权值,区间值是边权值。求最长路。有个细节问题是,如何判断无解情况,需要统计入队次数来判断,否则将会一直更新下去造成TLE。

代码

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int INF = 0x3f3f3f3f;

#define REP(i,n) for(int i=0;i<(n);i++)

const int MAXN = 1e5+10;
int dis[MAXN],first[MAXN],vis[MAXN];
int tot,n,m,u,v,w;
int a[MAXN],sum[MAXN],cnt[MAXN];

struct Node{
    int v;
    int w;
    int nxt;
}edge[MAXN*10];

void addedge(int u,int v,int w){
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].nxt = first[u];
    first[u] = tot++;
}

int SPFA(int s){
    queue<int>q1;
    memset(dis,INF,sizeof(dis));
    memset(vis,0,sizeof(vis));
    q1.push(s);
    dis[s] = 0;
    while(q1.empty()==0){
        int u = q1.front();
        q1.pop();
        vis[u]=0;
        for(int i=first[u];~i;i=edge[i].nxt){
            Node cur = edge[i];
            if(dis[cur.v]>dis[u]+edge[i].w){
                dis[cur.v]=dis[u]+edge[i].w;
                if(!vis[cur.v]){
                    vis[cur.v]=1;
                    q1.push(cur.v);
                    if(++cnt[cur.v]>sqrt(n)){
                        return -1;
                    }
                }
            }
        }
    }
    return 0;
}

void init(){
    memset(first,-1,sizeof first);
    memset(a,0,sizeof(a));
    memset(sum,0,sizeof(sum));
    memset(cnt,0,sizeof(cnt));
}

int main(){
    while(~scanf("%d %d",&n,&m)){
        tot=1;
        init();
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&u,&v,&w);
            addedge(v,u-1,-w);
            addedge(u-1,v,sum[v]-sum[u-1]);
        }
        for(int i=1;i<=n;i++){
            addedge(i,i-1,0);
            addedge(i-1,i,a[i]);
        }
        int flag = SPFA(n);
        if(flag==-1)    printf("Bad Estimations\n");
        else    printf("%d\n",-dis[0]);
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/caomingpei/p/9447358.html